here is the new way to "freeze" plugins: i will use the Felix
webserver (src/tools/webserver.flx) as an example.

First you write a loader:

//////////////////////////////////////////////////////
// webserver plugin linker

class WebserverPluginSymbols 
{

  // We have to do this dummy requirements because static
  // linking removes the information in Felix.
  requires package "re2";
  requires package "faio";
  requires package "flx_arun";

  open Dynlink;

  // Now add all the symbols.
  proc addsymbols ()
  {
    static-link-plugin 
      fdoc2html,
      flx2html,
      fpc2html,
      py2html,
      ocaml2html,
      cpp2html,
      fdoc_scanner,
      fdoc_slideshow,
      fdoc_heading,
      fdoc_fileseq,
      fdoc_paragraph,
      fdoc_button
    ;

    // webserver
    static-link-symbol webserver_create_thread_frame in "webserver";
    static-link-symbol webserver_flx_start in "webserver";
    
  }
}

// Add the symbols
WebserverPluginSymbols::addsymbols;

// Now invoke the webserver!
println$ "Running webserver";
val linstance =  Dynlink::prepare_lib("webserver");
println$ "Webserver prepared";
var init: cont = Dynlink::get_init linstance;

Fibres::chain init;
////////////////////////////////////

Notes 1. When you generate an object file (.o, .os, .obj extensions)
and later link it, information about the libraries it is required to link
to is lost. In C/C++ it was never there, you have to use a command line
to provide the libraries containing required symbols to satisfy external
references.

With Felix, a whole program compilation retains that information
all the way through to the linkage step. However when you flddle
the "flx" command to produce object files, the information is again
lost (the dependencies are generated but not tracked, at least at 
the moment). 

The first half of the workaround is to re-introduce the dependencies,
which is done with the requires clauses you see. These are requirements
of the webserver. This forces linkage of the appropriate libraries.

Notes 2.  The plugin variant of this command requires four entry
points in the plugin. If it is named PLUGIN these entry points are

        PLUGIN_create_thread_frame
        PLUGIN_flx_start
        PLUGIN_setup
        PLUGIN


These create the thread frame (global data object) and do C++ member
initialisation, do custom Felix initialisation which involves executing
dynamic initialisers and running "global" code, a special setup
function that accepts a string that can further configure a plugin,
and finally the core entry point. 

In Felix code when writing a plugin, you must export the last two
of these symbols:

export fun setup of (string) as "PLUGIN_setup";
export fun main of (string * string) as "PLUGIN";

The first two are exported automatically for all libraries.
Plugins are in fact just ordinary Felix programs generated
as shared libraries (the default), except for these two extra
entry points.

Commonly, the core entry point is an object constructor that
returns an object whose methods are used to access the
services offered by the plugin.

Notes 3: The meaning of the previous Note 2 is evident
for the webserver itself. That was written as a "program" not
as a plugin. Since it lacks the setup and core entry points,
we have to create the linkage with the lower level variant:

    // webserver
    static-link-symbol webserver_create_thread_frame in "webserver";
    static-link-symbol webserver_flx_start in "webserver";

otherwise we'll try to link "webserver_setup" and "webserver" which
don't exist. Actually we could modify the webserver to add these
entry points so it could be used both as a program and as a plugin.

note again the central theme: there's nothing special about programs
or plugins in Felix: they're all (shared) libraries. you have to link
one of:

        flx_run
        flx_arun

to make an  executable program.
 
For static links this actually requires a special linker thunk similar to
the one above we wrote by hand, only in this case the compiler generates
the thunks for you (the thunks are needed to map the fixed symbols
that flx_run requires into the names used in the library, which are just
the program name followed by _create_thread_frame and _flx_start.

Note 4: As a design issue, Felix requires all plugins use distinct
entry points. With most dynamic linkers this isn't required, since the
library name identifies the symbol table.  However it is always required
for static linkage (duplicate symbols can't be tolerated), at least without
weird linker and OS specific hackery.

Note 5: the invocation of the webserver is done by

val linstance =  Dynlink::prepare_lib("webserver");
var init: cont = Dynlink::get_init linstance;
Fibres::chain init;

The first line prepares the library. This will usually do a dynamic load
of the DLL. However in this case, we've added "webserver" to the
Felix symbol table so it will not dlopen() the webserver: its already
loaded by static linkage. The function then executes

        webserver_create_thread_frame

which create a new thread frame object on the heap and returns it.
This is the thing that holds the global variables. A pair consisting
of the library code and a thread frame object is called an instance.
Its exactly the CS and DS segment registers on Windows 3.1
programming model. This is also known as Harvard Architecture.

This separation is mandatory for re-entrant code. 
Unix uses the archaic von Neumann architecture which is the
cause of all its problems. It's enshrined in C, which is the cause
of most of ITS problems.

Ok, so, the get_init call extracts from the instance another pair:
the flx_start symbol and the thread frame, represented by a value
of the type "cont", which is a continuation. This is a program
"read to run".

Finally, we run that continuation by passing it to the scheduler,
and discontinuing the currently executing continuation:
the chain function does this job. This loses the current
code position and thread frame. In functional terms its a tail
call.

========================

So now you have a detailed explanation how the code works,
here's how to link it: you say "make weblink":


weblink:
        build/release/host/bin/flx --test=build/release -c --nolink --static \
                -ox build/release/host/lib/rtl/webserver 
src/tools/webserver.flx 
        build/release/host/bin/flx --test=build/release -c --static -ox 
build/release/host/bin/weblink \
                build/release/host/lib/rtl/fdoc_heading.o \
                build/release/host/lib/rtl/fdoc_button.o \
                build/release/host/lib/rtl/fdoc_fileseq.o \
                build/release/host/lib/rtl/fdoc_paragraph.o \
                build/release/host/lib/rtl/fdoc_scanner.o \
                build/release/host/lib/rtl/fdoc_slideshow.o \
                build/release/host/lib/rtl/fdoc2html.o \
                build/release/host/lib/rtl/flx2html.o \
                build/release/host/lib/rtl/py2html.o \
                build/release/host/lib/rtl/cpp2html.o \
                build/release/host/lib/rtl/ocaml2html.o \
                build/release/host/lib/rtl/fpc2html.o \
                build/release/host/lib/rtl/webserver.o \
                src/tools/weblink.flx

The first step creates webserver.o object file. The usual webserver is built
from a webserver.os file. This is an object file with position independent code,
set with gcc and clang using -fPIC switch. We don't need that for static link
so we build an ordinary .o file. [Felix uses .os for position independent object
files, the "s" standards for "shared" because these files are used to build
shared libraries]. The distinction only exists on some platforms, such as
x86_64 and PPC. Its more crappy technology inherited from von Neumann
architectures, Unix, and C we should have got rid of decades ago.

The second step compiles our linkage thunk, and links it to the webserver
object file we just made and all the required plugins. Felix build system
(the NEW one written in Felix ONLY) builds the plugins as both 
PIC and non-PIC code. Here we use the non-PIC objects.

Again note: Felix loses the information required to auto-link the plugins:
in fact Felix doesn't know what plugins the webserver uses since they're
dynamically loaded by string name. That's why we had to make the
weblink loader thunk.

The final product is a fully statically linked webserver called "weblink"
which does not do any dynamic loading with dlopen() and so doesn't
need any LD_LIBRARY_PATH to operate. In other words, we've built
a stand-alone executable which can be deployed or delivered to 
clients without worrying about its execution environment.

==========================================================

In the future, I may look at simplifying this again. Its a serious pain having
to manually link the plugins. It is trivial to use an ordinary static link
library of plugins instead, however this only works if the complete
set of plugins is known. By their nature, users can write their own plugins.

You should also note: you can delete any of the plugins in the
static-link-plugin statement. Just delete the lines. The webserver
produced WILL STILL WORK .. it just needs to dlopen() the library.
This is transparent, modulo linking the library and setting up
the LD_LIBRARY_PATH.

In particular PARTIAL static linkage is quite possible.

I have done all this work for two reasons:

(1) I want the webserver in particular to be stand-alone again.
It used to be, then I invented plugins and created a deployment problem.
I could never tell *which* plugins got loaded because they were installed
in /usr/local/lib. Felix DOES NOT INSTALL ANYTHING in /usr/local/lib 
any more.

In fact Felix now only installs ONE public entity: it installs "flx"
in /usr/local/bin. No other tools are installed. Everything else goes
in /usr/local/lib/felix/felix-latest.

(2) With the new build system (written in Felix) and new file layout,
shared sources and platform dependent stuff including libraries
and executables are cleanly separated into two directories.

This allows us to take the shared sources and build multiple
target binary directories with different configurations.
For example on the Mac the default configuration "host"
uses clang as a compiler, on Linux, gcc is used instead.

But I want to test gcc on the Mac. And clang on Linux.

Now, with the new setup, I can modify "flx" to accept a target
option, and with some fiddling, use the Felix build system
to generate additional targets.

In fact we already have, and the build system uses, plugins
for various toolchains, only "flx" does not use these yet.

Why? Because THEY WERE ONLY AVAILABLE AS DYNAMIC
LOAD PLUGINS.

Which would mean "flx" was no longer a stand-alone executable,
but depended on installed plugins and LD_LIBRARY_PATH.

Which would make it very tricky to use "flx" to build Felix.
And it would make it impossible to just install "flx"
in /usr/local/bin and have it "just work".

BUT NOW .. we can statically link plugins. So I am now free
to rewrite "flx" to use plugins knowing that I will not be losing
the ability to produce a stand alone executable.

====================================================

So, for the last 3 months I have struggled with several apparently
discrete issue: plugins, static linkage, build systems, compiler
toolchains, file system layouts, cross-compilation, and installation.

All these issues are related as you can now see. Progress in one
requires progress in the others. We're not there yet, but we're
well on the way to an enterprise level Felix installation.

The next BIG issue, which is just coming into scope, is again
the build and installation system, but this time with a greatly
expanded view: to split up the code base into separate pieces.

In other words, package management. We have Mike Maul's
package management tool scoop as an entry point. Like "flx",
scoop will have to use plugins, but now it can take advantage
of the static linking ability. This is essential for bootstrapping.

Ultimately, the core build, flx, and scoop, need to be integrated.
In an ideal world, you can run stuff straight off the internet.
Downloading and installation are transparent, just as
compilation is made more or less transparent by the current 
flx tool.

There are important issues here to deal with like security
and stability. So as usual we start off with pieces of automation
manually driven and gradually integrate.

Although automatic execution of code off the internet seems
scary, i would like to point out ...

        you're already doing it every day!!

In fact one of the PRIMARY advantages of Javascript in building
web pages is that you can just "source" whole libraries off
the internet, such as JQuery. Its a plugin and package, you
can provide it on your own server, but most web developers
don't.

Javascript is rewriting the whole notion of computing.
Using it you can program platform independent GUI's.
You're leveraging the huge amount of work developing
browsers and the domain object model to provide a more
or less platform independent GUI.

Of course there's a whole lot of CRAP behind this like AJAX.
What horrible crap to do http exchanges "per character"
to get interactivity. HTTP just wan't designed for that.

Never-the-less .. it has been made to work and it demonstrates
very clearly a level or re-usability never achieved at such a huge
scale before. The best we had in prior lives was CPAN for Perl,
and a very poor approximation in the form of Debian and apt.



--
john skaller
skal...@users.sourceforge.net
http://felix-lang.org




------------------------------------------------------------------------------
AlienVault Unified Security Management (USM) platform delivers complete
security visibility with the essential security capabilities. Easily and
efficiently configure, manage, and operate all of your security controls
from a single console and one unified framework. Download a free trial.
http://p.sf.net/sfu/alienvault_d2d
_______________________________________________
Felix-language mailing list
Felix-language@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/felix-language

Reply via email to