These class libraries may contain embedded C-code; either for performance reasons or to interface to other C-libraries.
This document will give you information and a step-by-step guide on how to compile your classes to machine code, how to create & package class libraries for distribution and how to include such a binary class library in your system.
Notice, that beside speed advantage and the ability to include inline C code, the semantic of binary compiled code is equivalent to that of interpreted bytecode. All language features (such as context handling, block handling, stack unwinding and exception handling) are available and perform transparent whether methods are compiled statically or interpreted (or compiled dynamically from bytecode by the JIT translator).
Compiled object files are bigger than bytecode files. Therefore, the memory
requirements are bigger if machine compiled code is used - at least if a single
Smalltalk/X application is running.
However, on systems which support shared
libraries, this code is shared amongst all those applications (which is not
the case with interpreted bytecode), therefore, depending on how many such
applications are executing, there may be a memory advantage - even with larger
object files.
Also, it must be considered, that statically compiled machine code is located
in the code segment of the program - i.e. it will only paged into the
physical memory when needed and requires much less memory in the objectMemory
area - therefore, even when requiring more memory virtually,
there is actually often less real memory required.
In addition, static compiled machine code is not loaded into the objectMemory, and therefore not to be considered by the garbage collector. Especially for big projects, this may reduce GC overhead considerably.
Another positive aspect of statically compiled machine code (in contrast to
just-in-time compilation) is the guaranteed maximum response time,
in which an invoked method is called.
If methods are compiled at
execution time, chances are non-zero, that some method(s) have to be compiled
on the first call, and an additional delay is introduced.
(unless you have visited all methods previously and the code cache is big
enough to hold all methods
- but then, the dynamic memory requirements of
a just-in-time system are in the same order
as with static compilation ...).
This defined behavior is the main reason for industrial users to
prefer static over dynamic compilation.
Configuration Files
This is only relevant for Unix systems.
The build process for Windows systems is described below.
Stc can be used like any other (batch-) compiler by
calling it directly from the UNIX/DOS command line. However, since there are
many possible command line arguments and configuration settings that have to
be managed, it is easier and suggested that you use Makefiles
to compile classes and build systems.
The system as delivered includes shell scripts and make rules to
handle different architectures and configurations.
To avoid the need for rewriting all Makefiles for a new architecture,
all relevant values have been splittet into machine dependent parts
and project (or library) dependent part. The machine dependent parts are included
from the configurations folder. The library contents is defined in
"Make.proto
" and "Make.spec
" files.
The configurator (called "CONFIG
") creates appropriate symbolic links
named "myConf
" and "vendorConf
" in the configuration folder.
To do so, the configurator uses so called config and rule files, which define all architecture and configuration dependent settings.
Make rules are found in the "rules
" subdirectory;
config files in subdirectories of "configurations
".
You don't have to care for the rule files normally - they are independent
of the configuration.
The config files are organized by machine architecture and configuration.
They define things like C-compiler flags, include paths,
classes to be included in the building process, additional link libraries etc.
In addition, package-lists are used to control which classLibraries are to
be built into the system.
These are found in the "configurations/PACKS
" subdirectory.
Package lists define which objects should be included,
config files define how these objects are built.
The generic paths of config files looks like:
For example, the linux definitions for linux versions above 1.0,
with C-optimizer turned on are taken from:
configurations/COMMON/defines
configurations/architecture/COMMON/defines
configurations/architecture/configuration/defines
and so on.
The name of the directory has no semantic meaning - its only for human
readers. If you want to create additional configurations, please do so in a new
subfolder. Do not change existing configuration files (because your changes will be
lost, when you install a new version of ST/X).
configurations/COMMON/defines
configurations/linux/COMMON/defines
configurations/linux/linux1.x-opt-xxx/defines
When a Smalltalk is built, the name of the configuration is stamped into the
executable - look at the launcher's
aboutBox, to see the configuration
of your system.
Summary:
CONFIG
architecture package configuration
or:
CONFIG
auto
which tries to determine a reasonable configuration based on your OS-dialect.
To get available configurations listed
(if you are uncertain, which architectures and/or configurations are
available) call CONFIG
without argument(s).
configurations
/architecture"
defines
" in this directory.
Take any existing "defines
" file from the same architecture to
start with.
CONFIG
architecture package mysystem"
to recursively create new Makefiles
.
Remember, that for all of the following definitions, useful defaults
are setup in
"configurations/COMMON/defines"
and "configurations/arch/COMMON/defines"
.
In normal situations, no changes are required.
However, if you need anything to be changed, do so in your configuration
specific config file; never in a common one.
Since later definitions overwrite previous ones, you can always change
things by adding a define to the private defines file.
The following is a short extract - there are many more things that
can (but are not required to) be changed:
The common configuration files define things like relative path names
(within the ST/X file hierarchy), make rules etc.
CC= defines the c-compilers name
(not recommended to change)
O=.o extension for object files
(not recommended to change;
- usually ".o" on UNIX, ".obj" on MSDOS)
#
# for "make install" only:
#
DESTBINDIR= where are binaries to be installed
DESTLIBDIR= where are libraries to be installed
DESTINCLDIR= where are include files to be installed
DESTMANDIR= where are man pages to be installed
#
# for additional directories and libs (see below)
#
OTHERLIBS= specifies names of other (c-)libraries to be
included in the final link
#
# Xlib stuff
#
XINCLUDE= path to X include files
LIBX= whats the name of your X-lib
LIBXEXT= whats the name of your Xext-lib
X_LIB_DIR= path to where X-libraries are found
#
# stc compiler flags
#
STCCONFOPT= arguments passed to stc
#
# c compiler flags and (preprocessor) defines
#
DEFS= addidtional defines passed to the c-compiler
XDEFS= like DEFS, for XWindow-related files/classes
OPT= addidtional optimizer flags passed to the c-compiler
(typically something like -O6 -m486 ...)
#
# additional individual class objects
#
EXTRA_CLASSES= extra individual compiled classes to be
included in the final link. These are classes
that do not come from class libraries.
EXTRA_OBJ= the corresponding object file names
Most important (and of general interest) are the following definitions,
which can be used in "Make.proto
" files:
INCLUDE= the relative path to the include directory
STC= the relative path to the stc compiler
(usually: $(TOP)/stc/stc, but may be redefined to refer
to an stc in some other directory)
LIBBASICDIR= the name of the directory where the basic classes
reside. Similar definitions are found for all the
other standard packages.
BIN_O= the suffix of individual binary object files (cc targets)
("
.o
" with unix; different on other architectures).
Always use this in your "Make.proto
" files
to remain architecture independent.
O_EXT= the suffix of class library files (stx packages)
(".o
" or ".obj
" on systems
which do NOT support shared libraries;
".so
" or ".sl
" on systems which do)
What is in a Package File
This section can be skipped - it is only of interest if
if you want to change the set of libraries & modules which are
included in the Smalltalk executable.
As mentioned above, the package files (in "configurations/PACKS
")
control which class libraries and additional C libraries are to be
built and included in the resulting executable.
The entries found there are:
The above information may not be up to date, when you read this document.
WORKSTAT1= name of the primary Display driver class
(typically: XWorkstation)
WORKSTAT2= name of the secondary Display driver class
(typically: GLXWorkstation)
EXTRA_CLASSES= names of additional classes, which
are to be included from individual object
files (not class libraries)
Typically: XWorkstation GLXWorkstat
EXTRA_OBJ= names corresponding object file names
Typically: $(LIBVIEWDIR)/XWorkstat$(BIN_O)
OTHERLIBDIRS= other directories, which the build process
should visit and `make'
CLASSLIBDIRS= directories containing class library sources
and `Make.proto' files to be built.
LIBOBJS= names of classLibrary objects to be included
in the link.
LIBLIST= names of the packages found in above objects.
Typically, these correspond to the file
names, but in rare cases, these names could
differ.
Examine the files found in
configurations/PACKS
for more information.
Recompiling the System in Part or Fully
Although recompilation of the complete system is easily done with:
Unix:
cd TOP
make clobber - to clean everything
make - make a target system as set by CONFIG
Windows (Borland compiler):
cd TOP
bmake clobber - to clean everything
bmake - make a target system
Windows (VisualC compiler):
cd TOP
vcmake clobber - to clean everything
vcmake - make a target system
Windows (MinGW compiler):
that takes a long time on some machines. For local changes, you will get a feeling
of what needs to be recompiled. For example, after adding a method to
a class in "
cd TOP
mingwmake clobber - to clean everything
mingwmake - make a target system
libbasic
", only a recompilation of that class and a relink of
the libbasic class library is required
and you can skip over the full remake.
In this case, you can also type:
which is much faster, as not all directories are visited.
cd libbasic
make (bmake / vcmake / mingwmake)
cd ../projects/smalltalk
make (bmake / vcmake / mingwmake)
However, whenever instance variables are added to classes which are subclassed somewhere else, these subclasses have to be recompiled as well. The makefile generator has added a dependency section to your Make.proto files - so only subclasses will be recompiled in the full make.
When new source files (i.e. new classes) are added to a classLibrary,
add its name to the "Make.proto
" file
(or regenerate a new one) and type:
...
cd theDirectoryWhereTheSourceWasAdded
make (bmake / vcmake / mingwmake)
...
cd ../projects/smalltalk
make (bmake / vcmake / mingwmake)
What is in a Make.proto File
A Make.spec
file lists the classes to be included in a library and
possibly subprojects (subdirectories) in which more libraries are to be built.
The Make.proto
contains dependency information, additional rules
and definitions for Unix builds.
Make.proto
files are not used with Windows builds - there, all information
is contained in the bc.mak
file
(originally the name was short for "borland-compile", but it is now also used for VisualC
and MinGW builds..
Since all architecture and configuration specific things are handled by the
config files, Make.proto
and Make.spec
files need not (should not) include any system dependencies.
Therefore, "Make.proto"
and "Make.spec"
files are rather short.
They only define the name of the library to be created, the subdirectories
(if any) that should be visited and the names of the object
files, which make up the library.
The required defines are:
optional are definitions which change compilation flags:
TOP= defines the position relative to the TOP
directory (see example below)
ALLSUBDIRS= names of subdirectories, where Makefiles
are to be created (if any).
SUBDIRS= names of subdirectories (if any), which should
be visited during the build process.
LIBNAME= name of the class library which is to be
created by this makefile.
(empty, if this is not for a classlib)
all:: default rule; usually, this simply calls
for the 'classLibRule' target, which creates
the required help files, object file(s) and
a prelinked classLibrary (possibly a shared one).
Additional C file targets should go here
OBJS= names all object files which are to be
built and/or included in the classLibrary.
optional but highly recommended rules are:
STCOPT= stc options; defaulted in COMMON/defines
There is usually no need to redefine this in
individual Make.proto files.
STCLOCALOPT= stc options to be used in addition
to standard settings;
additional package or optimization
flags should be given here.
For example: 'STCLOCALOPTS=+optspace3'
For example, the "
clean: a rule to cleanup all intermediate files,
but not the library targets - so that the
executable can be relinked without recompilation.
Typically, all object files are removed here.
clobber: a rule to cleanup everything
Ideally, the directory is left in the state it
had initially.
(Makefiles are usually kept)
Typically, all object files and library files
are removed here.
# BEGINMAKEDEPEND the "
make mf
" rule will
# ENDMAKEDEPEND insert dependency information in between
those. Always include those two keywords at
the end of your proto file.
Make.proto
" for the basic class library
looks (somewhat) like:
TOP=.. - its directly under TOP
SUBDIRS= - I have no subdirectories to make
LIBNAME=libbasic - thats the name of the classlibrary
STCOPT=$(LIBBASIC_STCOPT) - override default options
(LIBBASIC_STCOPT is +optinline)
STCLOCALOPT=-P(stx:libbasic) \ - define the package of all classes
-warnGlobalAssign \ compiled here, turn off some warnings
+optinline2 and turn on more inlining
all:: classListRule - default rule: create abbrev file
objects, classList and a prelinked
class library
OBJS= Object.$(O) \ - names all object files which are
Boolean.$(O) \ to be created from corresponding .st
... files. Notice the $(O) instead of .o;
This allows the same Make.proto to be
be used on MSDOS and Mac systems (where object
files are named differently).
# BEGINMAKEDEPEND - template for dependencies
# ENDMAKEDEPEND
How to get an Initial Make.proto File
There are three ways of optaining a Make.proto
file;
only the first is now recommended.
The other 2 are for experienced hackers, who know exactly what they are doing.
Make.proto
and edit as required
stmkmp
('st-make-makeProto') script, which is
found in the rules
subdirectory.
The stmkmp
scans the current directory for Smalltalk-source files
(files ending in '.st') and generates a Make.proto
file
for them (it was used initially, before the UI tools were present).
Creating Binary Class Libraries
The following gives step-by-step information on how to add a new class
library. For all of your new classes, this is the recommended way of
doing things. Adding classes to existing directories may lead to problems
and/or added work whenever a new ST/X release is delivered and installed:
you would have to reedit all of your changed Make.proto files.
Lets assume, that your new library is to be called "libfoo
"
and shall contain the classes called
Foo
, Bar
and Baz
.
First of all, you should not package it under the "stx:" module. (i.e. do not create it under the "stx" folder). The "stx:" module prefix is reserved for the base system.
Use a private module prefix (usually your company name). This implies, that the files will be located in a sibling folder of the "stx" folder (i.e. a subfolder of "stx"'s parent folder).
In the following description, let's assume this is called "myCompany".
Another good idea is to put classlibraries which are useful for multiple projects into a separate folder (such as a "common" folder).
Be warned again: to avoid additional work and frustration, when a new ST/X release is installed, please DO NOT create your "libfoo" directory below the ST/X top.
Thus, your folder hierarchy should look like:
someDir/stx/ - home of stx
... libbasic - as delivered
... configurations - as delivered
... rules
...
someDir/<myCompany>/common - your common classLibs
someDir/<myCompany>/<project1> - one of your project - beside stx
... <project1>/classLib1 - further subdivided if required
... <project1>/classLib2 - further subdivided if required
someDir/<myCompany>/<project2> - another project - beside stx
... <project2>/classLib1 - further subdivided if required
... <project2>/classLib2 - further subdivided if required
This setup will avoid any conflict, when additional classLibraries
are integrated from other vendors, or a new ST/X revision is installed.
(also, note that additional addOn packages
as delivered by eXept (such as ASN1, OSI etc.) will
go into "someDir/exept/...
" and not conflict with your files.
After all of this, let's assume that your new directory is called:
and it is locate beside the ST/X tree, which is:
.../myCompany/project1/libfoo
.../stx/...
Notice, that the plain "Save" menu function creates the files in your current
directory (which is usually "projects/smalltalk
").
So please use the "Save Each Into..." menu function.
Otherwise, you will have to move the files manually into "$TOP/myCompany/libfoo
".
Since stc requires that each class is in a separate source file,
the fileOut-category or fileOut-package functions cannot be used here
(as they create one huge file containing all classes).
Instead, use fileOut-each, which saves each class in a separate sourcefile.
Alternatively (if you have a SourceCodeManager configured), check the classes into the repository in the systemBrowser (possibly into a new module / module-package), open a terminal view (xterm / cmd-console), and check them out with the 'co' shell command.
For example, if you have a CVS repository available, you should check your classes into the repository directly from within the system browser ("Check In" menu function).
Make.proto
" files had to be edited manually.
This is no longer needed. Use the browser's "Fileout Built Support Files" menu function,
to create all required files
(this will create Make.proto
, Make.spec
, bmake
,
vcmake
and mingwmake
files).
If you have a source code repository, you should check the built support files into it right from within the browser.
libfoo
.
cd libfoo
make
this should (after a while) leave you with your new classLibrary libfoo
in that directory.
Depending on the architecture and/or configuration, the filename extension of the library varies; any of "If you plan to pass the compiled class library to others, all you have to distribute is thelibfoo.a
", "libfoo.obj
", "libfoo.o
" or "libfoo.so
" may be found there after the make. Don't care for this detail - the make rules create whatever is best for your architecture.
(For example, on some systems archives (".a" extension) lead to very long link times - on those, classLibraries are prelinked, relocatable objects (".obj"). Some do not allow ".obj", therefore ".o" is used. Finally, some support shared libraries named ".so", ".sl" or even ".dll")
libfoo
object just
created.
...
Smalltalk loadPackage:'<myCompanyName>:project1/libfoo'.
...
Alternatively, you can add a line to the "private.rc
"
or "startup.rc
" scripts, which loads the library at startup
time:
This is portable in that it determines the
required file suffix itself (".so" / ".sl" or ".dll", depending on the system)
...
Smalltalk fileIn:'name-of-your-class-object-file-without-suffix'.
...
However, the recommended interface is loadPackage:
because that will try hard to load the package, even if it is not compiled
to a binary class library (i.e. it will load from sources, if no dll is present).
...
Smalltalk loadPackage:'<myCompanyName>:project1/libfoo'.
...
On all systems which do NOT support dynamic loading, you have to leave any running Smalltalk, compile your class libraries, relink the Smalltalk executable and start a new (this time with those new classes being part of the built-in classes).
Since saved snapshot images are (currently) unusable after a system rebuild, it is now time to save all your work in source form (i.e. fileOut or checkIn all other classes and make certain that you can reconstruct your universe later from these and/or the changes file).
The Smalltalk executable is rebuilt with:
cd projects/smalltalk
make (bmake / vcmake / mingwmake)
smalltalk -I"
to have it ignore any existing snapshot file.)
For your tests, always keep the old snapshot, Smalltalk
executable and dlls around by copying the "projects/smalltalk" folder.
Remember: snapshots only work with the corresponding binaries.
Adding more Classes
Once you are through the above hard work,
adding more classes is easy:
in the system browser, select the package definition class
(i.e. the class named "myCompany_project1_libfoo") and select the "Update Project Definitions" menu
item, found in the "class"-"generate" menu.
Then fileout or checkin the project (including the built support files),
open a shell window, checkout and remake the library there.
As above, a remake of the Smalltalk executable itself is only required
if your system does not support shared libraries.
If it does you can even switch to the new classLibrary in the running system,
by: unloading any old loaded classLibrary first
(using the launcher's "System"-"Modules" dialog),
and re-loading the new shared library.
WARNING:
Depending on the C-Code / C-libraries you have included, this reloading
may fail occasionally, if there are leftOver references to static data
or functions somewhere in the C-code.
This may even lead to a crash in some situations.
We highly recommend saving your work before any reload - over time,
you will get a feeling for that ;-)
Adding more Class Libraries (to the Base System)
Once you have written a number of private class libraries,
you may eventually want them to be automatically loaded upon startup.
Or, for deployable standalone applications, you may want to build an executable,
which has them already included.
You have multiple choices to realize this:
For each function to be called, you need one wrapper method, which
contains a single line such as:
this creates a Smalltalk method (typically on the class side),
which call the "StartPage" function, with one argument of "handle" type in the
the "gdi32.dll". The return value is an int32.
primStartPage:hIn
<apicall: int32 "StartPage" (handle) module: "gdi32.dll" >
The good news about this is that you can call any function without a need for
any compilation. It works with stc-compiled and with bytecode methods.
However, you have to correctly specify the argument- and return types,
and on Windows also the calling convention ("api" or "c").
This mechanism uses the libffi foreign function interface, and has therefore a little overhead in calling (a few microseconds).
The loading of the shared library is fully automatic. It will be done lazily, when the very first function inside that dll is called.
For this, put the C functions into the "primitiveFunctions" section of the class source, and wrapper methods to call them. Many ST/X methods use this mechanism, to tune special cases in separate C-functions. Take a look at the Image- or the XWorkstation classes for concrete examples. (for example, some decompressors are found in the Image-class's primitiveFunctions section)
Place the code as custom functions there, and call them via "ExternalFunction callCustomFunction:".
We recommend a combination of the "Inline" and "Code in the class library" mechanisms. However, this choice is only a good one for a small number of C functions, of which the source code is available.
primitiveFunctions
section to your Smalltalk
source, define the functions there.
This has the advantage, that the C functions are included in the object module itself, so that no further libraries have to be linked in. Especially, if you plan to offer your binary library to others, this makes the distribution easier.
libfoo
directory), change the all:
rule in
that "Make.proto
" to:
all:: classLibRule myCLibs
and add a rule for the libraries:
myCLibs::
(cd subdir1 ; make)
(cd subdir2 ; make)
...
For automatically generated built support files,
you will need the specify them in an "additional_rules" method of your project definition class
(take existing project definitions as a guideline, for example the one in stx_libview).
Although possible, we recommend NOT to place the C source files directly in the directory where your ST sources are (since you may later want to cleanup stc's temporary files, with "rm *.c" and could thereby delete your files by accident).
OTHERLIBS
" in the config file.
For example, if you have a C library called "libUseful.a
",
placed under TOP/libfoo/cStuff
, the line should look like:
OTHERLIBS=$(TOP)/libfoo/cStuff/libUseful.a
Of course, if the library is located in some standard place
(i.e. in /usr/lib
or /usr/local/lib), you
can also write:
OTHERLIBS=-lUseful
Read the C-compiler's and linker's man pages if you need more information on
this.
All OTHERLIBS are linked in the order specified; thus, if you have
dependencies between your extra libraries, these may be fixed by
changing the order.
For example, if you have two libraries "libUseful1.a
"
and "libUseful2.a
",
of which the second needs entries in the first,
you will get a link error if the config define looks like:
in this case, change the order as in:
OTHERLIBS=$(TOP)/libfoo/cStuff/libUseful1.a \
$(TOP)/libfoo/cStuff/libUseful2.a
OTHERLIBS=$(TOP)/libfoo/cStuff/libUseful2.a \
$(TOP)/libfoo/cStuff/libUseful1.a
An Example for C Interfacing
In the following, an interface to C functions which are compiled
from separate sources is shown.
For the example, we assume that a directory "libfoo"
exists,
and the library is to be named "libfoo
" as well.
For better structuring, we place the C sources into a subdirectory of
"libfoo"
called "cstuff"
and create a
C library called "cstuff.a
" there.
The C sources are in "libfoo/cstuff/module1.c
":
and "
cFuntion1() {
printf("here is your C function call ....\n");
}
libfoo/cstuff/module2.c
":
The interface wrapping code for Smalltalk is in
"
cFuntion2(arg)
int arg;
{
printf("here is your 2nd C funtion; the args value is %d\n");
}
libfoo/CInterface.st
":
The "
Object subclass:#Cinterface
instanceVariableNames:''
classVariableNames:''
poolDictionaries:''
!
!Cinterface class methodsFor:'C calling'!
cFuntion1
%{
cFuntion1();
%}
!
cFuntion2:argument
%{
if (__isSmallInteger(argument)) {
cFuntion2( _intVal(argument) );
}
%}
! !
Make.proto
" to compile all of these is:
The C library is built by the following rules in "
TOP=..
LIBNAME=libfoo
all:: CSTUFF abbrev.stc objs genClassList $(OBJTARGET)
objs:: CInterface.$(O)
CSTUFF::
(cd cstuff ; make)
cstuff/Makefile
":
Thats it, after a "make" in libfoo, you will find
"
all: module1.o module2.o
ar rv cstuff.a module1.o module2.o
libfoo.obj
" (or "libfoo.so
")
and "libfoo/cstuff.a
"
ready for linkage.
To get those into the executable, your config file should have the
definitions:
The files of the example can be
found in "
OTHERLIBDIRS=$(TOP)/libfoo
PRIVATEOBJS=$(TOP)/libfoo/libfoo$(OBJNAME)
PRIVATE_SO=$(TOP)/libfoo/libfoo$(SO_NAME)
PRIVATELIBS=libfoo
OTHERLIBS=$(TOP)/libfoo/cstuff/cstuff.a
doc/coding/libfoo_example
".
For details on the primitive wrapper code, read
``How to write primitives & inline C code''.
Automatic building
Automatic creation and building via the project management is being prepared.
However, at this time, this feature is not fully implemented and things
should be done manually.
Once completed, you will be able to create per project directories,
Make.protos
and the sources by the click of a button.
Copyright © 1995 Claus Gittinger, all rights reserved
<cg at exept.de>