![]() |
|||
AROS Application Development Manual
Warning This guide has it's last major revision begin November 2006. Changes to the build system after this date will not be reflected in this guide. The AROS build systemIntroductionPurpose of the AROS build systemOne may wonder why AROS needs a build system. The answer is that it simplifies the building of more complex binaries and takes away a lot of the manual work otherwise needed to compile, link and copy the files. Additionally this system builds a dependency tree over a big source code tree and will build the binaries in the right order so that all dependencies are fulfilled when a certain binary is built. This system also makes the source code tree modular: you can add or remove certain directories and the build system will take up these changes (of course one always has to take care one does not delete code other parts are still depending on). Components of the build systemAROS uses several development tools in it's build system. A short list of the most important components is:
To illustrate how these tools interact which each other we will explain what happens under the hood when you compile the HelloWorld program given in the previous chapter. If you followed the tutorial for the building the program with the build system you first added the local/helloworld directory with some source files; afterwards you called make local-helloworld in the top make directory. During the execution of this command the following steps were taken:
Build system tutorialIn this section a description is given of the most important macros for AROS application development. The purpose is not to give an in-depth discussion but enough information to be able to perform most of the application development needs. An in-depth discussion of the build tools is given in the reference section in this manual. Basic file syntax and setupAs was seen in the helloworld example above you use the build system by putting a mmakefile.src file in the directory containing your source files. For the AROS build system to notice your file the directory containing it has to be below the AROS main source directory (the name of this top directory is most likely AROS). So for the moment you need to get hold of the AROS source code to be able to use the build system which is discussed in another chapter. A good place to put your own source code is below the AROS/local directory as was done for the helloworld example. An AROS mmakefile has to start with the following line: include $(TOP)/config/make.cfg ... The start line will set the environment in the makefile for AROS compilation. It has to be included because this environment is used by the metamake macros. After this first line most of the time one or more calls to a genmf macro will follow. Such a macro has the following syntax: %macro param1=... param2=... ... When the mmakefile.src file is translated into a mmakefile file the line above will be replaced with make commands defined by the macro. A macro is called by it's name followed by zero or more parameters with an optional value assigned to the parameter. The order of the parameters is not important and not all parameters defined by a macro have to get values; a default value will be used when a parameter is not provided. Some parameters may be mandatory and an error message will be generated when it is left out. One macro call can be spread over several lines by ending a line with a backslash and continue the macro on the next line. So the example macro call could have been written also as:
%macro \
param1=... \
param2=... \
...
Normally no spaces are allowed in the values given to a parameter, if that is needed one has to enclose the list by double quotes ("). In the following chapters the most important high level commands will be discussed. Only the most important parameters for the macros will be discussed, a list and description of all parameters of a macro will be given in the reference section. Building an AROS programYou can build a program by using the following macro in your mmakefile.src file: %build_prog mmake=MetaTarget progname=Prog files=SourceFiles This will build a program named Prog from the list of SourceFiles. By giving the mmake argument the MetaMake program will see that this program can be built by MetaTarget; e.g. doing make MetaTarget in the top AROS source directory will build the program. When typing this command also the dependencies will be built every time you want to recompile this module. Additionally a MetaTarget-quick meta-target will be defined that allows to build the program without that the dependencies will be rebuilt, this can save time when you are changing the source code of a program and want to rebuild it often. The list of files SourceFiles are the name of the C input files without the .c suffix. As explained above this list has to be enclosed by double quotes if it contains more then one file. By default the program will be put in the AROS binary tree in the same subdirectory from the top directory as the subdirectory relative where the source files are put into the source code tree. You can change this directory by specifying the targetdir=... argument. The latter argument has to contain a full path so most of the time it will start with $(AROSDIR)/ to put the program somewhere in the binary AROS tree. So if you want to put the program in the Extras directory you can do it this way: %build_prog ... targetdir=$(AROSDIR)/Extras As explained above the argument to a macro has to be enclosed by quotation marks if it contains more then one file. Currently the list can't be split over more then one line and often make variables are used to pass the list of files to build. The next three examples do the same only with different syntax. First the in-line list version:
%build_prog \
mmake=myprog progname=MyProg \
files="file1 file2 file3"
Now with using a make variable:
FILES := file1 file2 file3
%build_prog \
mmake=myprog progname=MyProg \
files=$(FILES)
Or with using the make line continuation:
FILES := \
file1 \
file2 \
file3
%build_prog \
mmake=myprog progname=MyProg \
files=$(FILES)
Building an AROS libraryAnother type of binary often built for AROS is a shared library. First a basic skeleton will be given of how to build a library then some handy extensions are given. Basic library skeletonA shared library is built with the %build_module macro with a line like this: %build_module mmake=MetaTarget modname=mylib modtype=library files=SourceFiles This macro can build different AROS module types like devices, Zune classes, HIDDs, ... but we'll focus here on a shared library and therefor we assume that you specify the modtype=library parameter. The mmake and the files parameter act the same as for the %build_prog macro. Additionally to the meta-target MetaTarget and MetaTarget-quick, also meta-targets MetaTarget-includes, MetaTarget-includes-quick and MetaTarget-linklib are defined. This allows to only build a subset of all the files normally generated. They will most of the time be used to specify dependencies. For building a shared library more information is necessary then given in the %build_module macro. This information is stored in another file that by default is called mylib.conf when modname=mylib is specified. This file can contain a lot of information but we'll give here a minimal example. More information can be found in the reference section of the %build_module macro. Here is an example .conf file: ##begin config version 1.0 ##end config ##begin functionlist void func1(LONG a, LONG b) int func2(char *s, ULONG a) ##end functionlist As you can see this is a file with two sections, each section starting with ##begin sectionname and ending with ##end sectionname. The section config is for providing the information needed by AROS when it wants to use the shared library about the library and for giving options to influence the type of module that will be build. In-depth discussion can be found in the reference section. The functionlist section gives a list of functions that will be included in the library and the list consist of the function prototypes. The order of the list is important because it will determine the place of the function in the lookup table. Also empty lines are important because an empty line in the functionlist will cause an empty slot in the table. Comment lines start with one '#' character and these lines will be ignored and do not give rise to an empty slot in the library's table. If all this information is present and you then execute the command make MetaTarget the following file will be generated ($(AROSDIR) corresponds with the directory for the binary tree):
Using non-standard typesIn the example given above standard C variable type or standard exec types were used for the arguments of the function. If you want to use your own types or types defined in other include files you will need to take extra steps. This can be done in the cdef section as shown in the next example where another include file is used: ##begin config ... ##end config ##begin cdef #include <exec/semaphores.h> ##end cdef ##begin functionlist ... BOOL func3(struct SignalSemaphore *sig) ##end functionlist The lines in the cdef structures are normal C code and they will be part of the generated include files before the library's function prototypes. You could also define your own structure like this:
##begin cdef
struct MyStruct
{
...
};
##end cdef
##begin functionlist
...
int func4(struct MyStruct *sig)
##end functionlist
When doing it this way the structure definition will be part of the generated include files. The recommended way to do it in AROS is to do the definition in a separate header file and then include that header file. One way to do it is to define your own file named libraries/mylib.h with the following contents:
#ifndef __LIBRARIES_MYLIB_H
#define __LIBRARIES_MYLIB_H
struct MyStruct
{
...
};
...
#endif /* __LIBRARIES_MYLIB_H */
This file is then copied as explained in another paragraph and then simply included by the cdef section: ##begin cdef #include <libraries/mylib.h> ##end cdef Functions with m68k register passingThe functions put into the library vector table up to now were regular C functions. In the amiga m68k days the parameters for library functions most of the time were passed in registers and not on the stack. For backwards compatibility you can also define functions where the arguments are passed in m68k registers. When your library is compiled for m68k it will use the specified registers, on other architectures own conventions will be used by either using registers available on that CPU or using stack based argument passing. Defining a function with m68k registers has to be done by adding the registers to the line in the function list and use macros for the header of the function in the source code. The line in the functionlist looks as follows: ##begin functionlist ... ULONG func5(ULONG a, STRPTR b) (D0,A0) ... ##end functionlist And the function in the source code is defined as follows:
AROS_LH2(ULONG, func5,
AROS_LHA(ULONG, a, D0),
AROS_LHA(STRPTR, b, A0),
struct Library *, MylibBase, 9, Mylib
)
{
AROS_LIBFUNC_INIT
...
AROS_LIBFUNC_EXIT
}
This macro has the name AROS_LHn with n the number of arguments passed to the function. The macros has the following arguments:
Using an extended libbaseOn AROS and other amiga-like systems every shared library has a library base. The base of a library contains the vector table and some data about the library used by the OS. It can also be extended with user defined data. This can be done by providing your own C struct for the type of the libbase. There are two config options that let you decide the type of the libbase: ##begin config ... libbasetype struct MyLibIntBase libbasetypeextern struct MyLibBase ... ##end config libbasetype is the type used internal in the library code, this type also decides how much memory is allocated for the libbase. If this type is not given struct Library is taken as default. libbasetypeextern is the type by external programs using your library. If not given also here struct Library is used as the type. Both the internal and the external type have to start with a struct Library structure. If the external type is given the first part of the internal type has to be the same as the external type. For keeping your library backwards compatible one can not change the external type of a library. You can only extend it once you have released a version of your library into the public. The internal type can be changed at will when all internal code of your library is adapted to the changes of the internal library structure. The external type also has be exported to the users of your library. This is the same as the usage of other non-standard types. On the contrary, the internal type oftentimes is not meant to be exported to the users, for this reason a cdefprivate section can be put in the config file. This way the library initialization code has all the information about your internal type without having the internal structure publicly exported. A common convention is to declare your internal structures in mylib_intern.h and then include this in the cdefprivate section. The mylib_intern.h would then include the following code:
struct MyLibIntBase
{
struct Library base;
...
};
And the config file the following section: ... ##begin cdefprivate #include "mylib_intern.h" ##end cdefprivate ... Using a per-opener libbaseUp to now only one libbase would be created for a library. All users who open the library get a pointer to the same library base. Sometimes one wants to have data that is different per opener of the library. This can be done by using a special option in the config section: ##begin config ... options peropenerbase ##end config The use of a base per opener of the library does not make much sense when not using an extended libbase. At the moment also the only way to pass the libbase to the functions of the library is by using m68k register passing. (Development is underway to also be able to get the libbase in library functions using the normal C argument passing). You could also add the libbase as an explicit argument to the function but this is not encouraged. Note On AROS the need for extended libbases is much less then on classic AmigaOS. On classic AmigaOS it was discouraged to use global variables in the library and to use the libbase for storing variables. On AROS global variables are handled fine so the use of an extended libbase is only needed for using a per-opener libbase. Library initializationSometimes you want to perform initialization when your library is loaded or when it is opened. You can use the same mechanism as for programs through the ADD2INIT and ADD2EXIT as in the next example:
static int InitFunc(void)
{
...
}
static void ExitFunc(void)
{
...
}
ADD2INIT(InitFunc, 0);
ADD2EXIT(ExitFunc, 0);
When you add this code in one of your source files the code in InitFunc will be executed when the library is initialized and the code in ExitFunc when the library is expunged. The return value of InitFunc indicates succes or failure, when it returns a zero (== FALSE) value it indicates a failure to initialize and the library will be unloaded again and not be usable. The ExitFunc may not fail and thus has no return value. Often one wants to initialize part of the libbase and therefor the methods discussed above are not appropriate. For libraries additional ways are available for adding initialization or clean-up code: static int InitFunc(struct Library *lh); ADD2INITLIB(InitFunc, 0); static int ExpungeFunc(struct Library *lh); ADD2EXPUNGELIB(ExpungeFunc, 0); static int OpenFunc(struct Library *lh); ADD2OPENLIB(OpenFunc, 0); static void CloseFunc(struct Library *lh); ADD2CLOSELIB(CloseFunc, 0); The InitFunc function will be called once during initialization and the ExpungeFunc once during expunge of the module. OpenFunc and CloseFunc functions are called resp. everytime the modules is opened or closed. InitFunc, ExpungeFunc and OpenFunc return a value indicating the success of the function. If InitFunc fails the module will be expunged, if OpenFunc fails the opening of the library will fail and if ExpungeFunc fails expunging the library will be delayed. If the latter happens the next time the expunge will be tried all registered functions for expunge will be called again. This means that if more then one function is registered and the second function returns 0, the first function will be called a second time the next time AROS tries to expunge the module. If you implement an ExpungeFunc that can return a 0 you also have to be sure that other ExpungeFuncs may be called more then once. If you look at the ADD2...LIB macros above you can also see that next to the function name there is an extra number. This number indicates the priority to call the function. InitFunc and OpenFunc with a higher number will be called after the ones with a lower number. For CloseFunc and ExpungeFunc the opposite order is used, e.g. higher numbers are called before lower numbers. The number is a signed byte, which means it has to have a value from -127 to 128. Most often this value can be kept at 0. If a per-opener base is used a copy will be made of your libbase every time the module is opened. InitFuncs will be called before the copy so initialization of values in the libbase will be seen by all the openers. OpenFuncs are called after the copy of the libbase and changes made to libbase are thus private to the opener. Copying include filesOften when writing a library extra includes have to be provided that can be included by the programs using your library by using #include <...> in the code. For this purpose a copy_includes macro is available. In the following line the arguments are given for the macro with the default values: %copy_includes mmake=includes-copy includes=$(INCLUDE_FILES) path=. dir= Here follows an explanation of the arguments and after that some examples will be given:
Some examples to make this more clear:
Using non-core libraries in programs or librariesBefore programs or other libraries can use another library that is not part of the core AROS libraries they have to add it to the list of libraries to use by using the uselibs argument for the %build_prog or the build_module macro. So if you want your program to use the mylib library you have to do it like this: %build_prog ... uselibs=mylib For a library it looks the same: %build_module ... uselibs=mylib Build system referenceWarning This reference manual is out of date and things have changed considerably. Please consult config/make.tmpl in the source code tree to see the current implementation. If you want to help updating this section, please contact us. MetaMakeIntroductionMetaMake is a version of make which allows to recursively build targets in the various directories of a project or even another project. It searches a directory tree for makefiles and all makefiles it finds for "metatargets". Then it tries to build all metatargets. You can also specify a program which converts "source" makefiles into makefiles before MetaMake will invoke make. Syntax of the makefileMetaMake uses normal makefile syntax but gives a special meaning to a comment line that starts with #MM. This line is used to define so called metatargets. The name of the makefile itself is defined in the MetaMake config file that is discussed in one of the following sections. There exist three ways of defining a metatarget in a makefile:
The line for the definition of a metatarget can be spread over several lines if you end every line with the \ character and start the next line with #MM. You can define a metatarget with the same name in several files. The metaprerequisites are then collected. If a metatarget is defined both with #MM and #MM- the #MM has priority. How MetaMake worksMetaMake is run by calling make in the root directory of the AROS source tree. At first MetaMake will build up a tree of all the makefiles present in a root directory and all subdirectories. At the same time it will also build a tree of all the metatargets and their dependencies. Next it will build all the metaprerequisites needed for this metatarget and then finally the metatarget itself. Actually someone can look at it that every metaprerequisite becomes a metatarget when it needs to be build. For each of these metatargets a walk through of all the directories is done. In every makefile where the metatarget is defined by the first or third way from the previous section make is called with the name of the target as a make target. When MetaMake calls normal make also two variables are defined. $(TOP) has the value of the root directory and $(CURDIR) the path relative to this root directory. Metatargets which aren't a prerequisite of another target aren't build by default. If you want to build such a metatarget you have to type make metatarget in the root directory of the AROS source tree. Autogenerated makefilesAnother feature of MetaMake is automatic generating a makefile from a source makefile. When the directory tree is scanned for all the makefiles in every directory it is checked if a makefile is present with a .src suffix added. If it is there and is newer than the makefile present in that directory a script will be called to regenerate the makefile from the source makefile. What script has to be called is defined in the configuration file. ExamplesThe next few examples are taken from the AROS project. Example 1: normal dependencies#MM contrib-regina-module : setup linklibs includes contrib-regina-includes This example says that in this makefile a contrib-regina-module is present that has to be build. Before building this metatarget first the metatargets setup, linklibs, ... have to be build; this ensures that the includes, linklibs etc. have to be present before this module can be build. Example 2: metatarget consisting of submetatargets#MM- contrib-freetype : contrib-freetype-linklib \ #MM contrib-freetype-graph \ #MM contrib-freetype-fonts \ #MM contrib-freetype-demos Here actually is said that the contrib-freetype metatarget requires building of linklib, graph, fonts and demos of freetype. If some extra work needs to be done in the makefile where this metatarget is, the definition can start with #MM and a normal make target contrib-freetype has to be present in the makefile. Also the use of the line continuation for the metatarget definition is shown here. Example 3: Quick building of a target#MM workbench-utilities : includes linklibs setup-clock-catalogs #MM workbench-utilities-quick : workbench-utilities When a user executes MetaMake with as argument workbench-utilities make will be called in all the directories where the metaprerequisites are present in the makefile. This can become quite annoying when debugging programs. When now the second metatarget workbench-utilities-quick is defined as shown above only that target will be build in this directory. Of course the user has then to be sure that the metatargets on which workbench-utilities depend are up-to-date. Configuration fileThe MetaMake configuration file has the path $(TOP)/mmake.config. A short explanation of its content:
GenmfIntroductionGenmf uses two files for generating a makefile. First is the macro definition file and finally the source makefile (mmakefile.src) where these macros can be used. The macros for AROS are in the file $(TOP)/config/make.tmpl. SyntaxIn general the % character is used as the special character for genmf source makefiles. Macro definitionA macro definition has the following syntax: %define macroname option1[=[default][\A][\M]] option2[=[default][\A][\M]] ... ... %end macroname is the name of the macro. option1, option2, ... are the arguments for the macro. These options can be used in the body of this template by typing %(option1). This will be replaced be the value of option1. The argument can be followed by a default value. If no default value is specified an empty string is taken. Normally no spaces are allowed in the default value of an argument. If this is needed this can be done by surrounding the value with double quotes ("). Also two switches can be given:
Macro instantiationThe instantiation of the macro is done by using the '%' character followed by the name of the macro to instantiate (without round brackets around it): %macro_name [option1=]value [option2=]value Two ways are possible to specify value for arguments to a macro:
When giving values to arguments also double quotes need to be used if someone wants to include spaces in the values of the arguments. Macro instantiation may be used inside the body of a macro, even macros that will only be defined later on in the macro definition file. Note In the definition of the genmf rules sometimes MetaMake variables are used as default variables for an argument (e.g. dflags=%(cflags)). This is not really possible in the definition file but is done by using text that has the same effect. AROS application development macros referenceHigh level mmakefile.src macrosAROS standard metamake targetsThe following metatargets are often used as prerequisite:
FIXME: complete Note These mega metamake targets were introduced in the beginning of the project. The usage of these metatargets is now considered as deprecated and should be avoided. One should try to use more specific targets for dependencies, e.g. if a certain program uses a certain library one should specify this library as a dependency of this program not all the linklibs by using the linklibs metatarget. Building programsThere are two macros for building programs. One macro %build_progs that will compile every input file to a separate executable and one macro %build_prog that will compile and link all the input files into one executable. %build_progsThis macro will compile and link every input file to a separate executable and has the following definition:
%define build_progs mmake=/A files=/A \
objdir=$(GENDIR)/$(CURDIR) targetdir=$(AROSDIR)/$(CURDIR) \
cflags=$(CFLAGS) dflags=$(BD_CFLAGS$(BDID)) ldflags=$(LDFLAGS) \
uselibs= usehostlibs= usestartup=yes detach=no
With the following arguments:
%build_progThis macro will compile and link the input files to an executable and has the following definition:
%define build_prog mmake=/A progname=/A files=%(progname) asmfiles= \
objdir=$(GENDIR)/$(CURDIR) targetdir=$(AROSDIR)/$(CURDIR) \
cflags=$(CFLAGS) dflags=$(BD_CFLAGS$(BDID)) ldflags=$(LDFLAGS) \
aflags=$(AFLAFS) uselibs= usehostlibs= usestartup=yes detach=no
With the following arguments:
Common%define common This adds some common stuff like a clean target to the makefile. The clean target only deletes generated makefiles. Building catalogsThe definition of the macro is as follows: %define build_catalogs mmake=/A name=/A subdir=/A \ catalogs="$(basename $(wildcard *.ct))" source="../strings.h" \ description="$(basename $(wildcard *.cd))" dir=$(AROS_CATALOGS) \ sourcedescription="$(TOOLDIR)/C_h_orig" With the meaning of the arguments as follows:
Example: %build_catalogs mmake=workbench-system-wanderer-tools-info-catalogs \ name=Info subdir=System/System/Wanderer/Tools Building iconsCreates icons. The images must be in PNG or ILBM format. The icon is configured from an additional text file with the name %(iconname).info.src. You can find the documentation of this file in $(TOP)/tools/ilbmtoicon/README The definition of the macro is as follows: %define build_icons mmake=/A icons=/A dir=/A With the meaning of the arguments as follows:
Example: %build_icons mmake=workbench-system-wanderer-tools-newdrawer-icons \ icons=newdrawer dir=$(AROS_WANDERER)/Tools The definition file has the name newdrawer.info.src. Building static linklibrariesBuilding link libraries is straight forward. A list of files will be compiled or assembled and collected in a link library into a specified target directory. The definition of the macro is as follows: %define build_linklib mmake=/A libname=/A files="$(basename $(wildcard *.c)) \ asmfiles= cflags=$(CFLAGS) dflags=%(cflags) aflags=$(AFLAGS) \ objdir=$(OBJDIR) libdir=$(LIBDIR) With the meaning of the arguments as follows:
Building modulesBuilding modules consists of two parts. First is a macro to use in mmakefile.src files. Another is a configuration file that describes the contents of the module. The mmakefile.src macroThis is the definition header of the build_module macro: %define build_module mmake=/A modname=/A modtype=/A \ conffile=%(modname).conf files="$(basename $(wildcard *.c))" \ cflags=$(CFLAGS) dflags=%(cflags) objdir=$(OBJDIR) \ linklibname=%(modname) uselibs= Here is a list of the arguments for this macro:
The module configuration fileThe module configuration file is subdivided in several sections. A section is defined with the following lines: ## begin sectionname ... ## end sectionname The interpretation of the lines between the ##begin and ##end statement is different for every section. The following sections are defined:
Compiling arch and/or CPU specific filesIn the previous paragraph the method was explained how a module can be build with the AROS genmf macros. Sometimes someone wants to replace certain files in a module with an implementation only valid for a certain arch or a certain CPU. The macro definitionArch specific files are handled by the macro called %build_archspecific and it has the following header: %define build_archspecific mainmmake=/A maindir=/A arch=/A files= asmfiles= \ cflags=$(CFLAGS) dflags=%(cflags) aflags=$(AFLAGS) compiler=target And the explanation of the argument to this macro:
Code shared by different portsA second macro called %rule_archalias allows to create a virtual architecture. And code for that virtual architecture is shared between several architectures. Most likely this is used for code that uses an API that is shared between several architecture but not all of them. The macro has the following header: %define rule_archalias mainmmake=/A arch=/A alias=/A With the following arguments
Examples
The file $(TOP)/config/make.tmpl contains more macros. See the comments in that file for usage. AROS portable makefile variablesThe file $(TOP)/config/make.cfg is usually included in all makefiles. It contains a lot of variables which are often used in this makefiles. The most important are the absolute paths for standard directories (e.g. AROS_C) and names for tools (e.g. MMAKE, GENMF). Platform depending definitions can be found in:
|
Copyright © 1995-2008, The AROS Development Team. All rights reserved. Amiga® is a trademark of Amiga Inc. All other trademarks belong to their respective owners. |