kjam build tool
WARNINGThis web site is designed for Firefox, Internet Explorer 6+, Safari, and Opera 9+. We apologize if this site does not render correctly on your browser.
KJam main page KJam performance information KJam is the world's fastest build tool. \n KJam is faster than perforce jam. \n Using KJam achieves the fastest compile time. \n Reduces your incremental build time. Improves compile time.\n You need it for faster compile times.\n KJam is the fastest build system.\n It can be called a distributed build system or distributed build software.\n KJam is a parallel build tool.\n You can distribute build tasks on a peer to peer network of build servers.\n It does a similar job to tools like Gnu Make, MSBuild, Ant, Nant, Maven, Scons, Rake, nmake, qmake, Electric Cloud, Xoreax Incredibuild.\n It builds in parallel like distcc. It is compatible with DJGPP and ccache.\n It produces the fastest build time.\n KJam results in the shortest incremental build times.\n Use KJam if your build is too slow, or if your build takes too long. It is also good if you have long link times.\n

This another real world example of a set of jamfiles for a project. This the rules file for a GCC based project, a Gameboy Advanced game. This project features some advanced techniques like machine generated source file, automatic handling of specialized files for localized verions of the game, and dependencies calculated for data files as well as source files. The user jamfile follows after the rules file.


#
#  Shared definitions for building with KJAM.
#

TOOLS_DIR ?= ../../tools ;
CONV_DIR ?= $(TOOLS_DIR)/conv ;
GBA_DIR ?= .. ;

if ( $(VERBOSITY) == "" ) VERBOSITY = ;

# The tools we are going to use ----------------------------

CC       ?= gcc.exe ;
CC32     ?= arm-elf-gcc.exe ;
LINKER   ?= ar.exe ;
AS       ?= as.exe ;
OBJCOPY  ?= objcopy.exe ;
CONV     ?= $(CONV_DIR)/bin/conv.exe ;
BRSEDIT  ?= $(TOOLS_DIR)/brs/brs_edit.exe "-v $(VERBOSITY)" ;
PERL     ?= perl ;

OBJ_DIR  ?= obj/ ;

# macros to control output ---------------------------------

# SILENCE = " | sed -n -e 's/warning/warning/p' -e 's/error/error/p'" ;

# the flags -----------------------------

INCLUDE_DIRS = $(INCLUDE_DIRS:A) ;

REL_INCLUDE_DIRS = $(INCLUDE_DIRS:X="[[:alpha:]]:.*") ;
ABS_INCLUDE_DIRS = $(INCLUDE_DIRS:I="[[:alpha:]]:.*") ;

COMMON_CCOPTS = -c -mthumb-interwork -W -Wall -Wno-unused --short-enums $(SAVE_TEMPS_FLAGS) ;

if ( $(CURFLAV) )  CURFLAV = $(CURFLAV:L) ;
else               CURFLAV = debug ;

if ( $(SAVE_TEMPS:L) != false )
  SAVE_TEMPS_FLAGS = -save-temps --verbose-asm ;

if ( !VERSION )
{
   #FIXME: get version id somehow
   VERID = "12" ; # $(shell svn info | grep Revision | sed "s/Revision: //g")
   FULLVERID = $(VERID).$(USERNAME).$(REGION).$(BUILD_CODE) ;
   VERSION = -DVERSION=\"$(FULLVERID)\" -DREGION=REGION_$(REGION) -DREGION$(REGION) ;
   ECHO $(FULLVERID) ;
}

if ( $(CURFLAV) == release ) 
{
   # shipping builds: highest optimization level, assertion removed in all code.
   # minimal debugging support. local variables are not visible in the deset bugger
   # because we optimized out the frame pointer register. Special release
   # only definition turned on
   # -O3 is fastest
   # -Os is optimized for size.
   
   BUILD_CODE = r ;
   CCOPTS   =    -Os $(COMMON_CCOPTS) -DNDEBUG -DRELEASE $(VERSION) --omit-frame-pointer ;
   CC32OPTS =    -O3 $(COMMON_CCOPTS) -DNDEBUG -DRELEASE --omit-frame-pointer ;
   
} else if ( $(CURFLAV) == optimized ) {
   
   # mostly optimized, but we put the frame pointer back
   BUILD_CODE = o ;
   CCOPTS   = -g -Os $(COMMON_CCOPTS) $(VERSION) -DNDEBUG ;
   CC32OPTS = -g -O3 $(COMMON_CCOPTS) -DNDEBUG ;

} else if ( $(CURFLAV) == debug ) {

   # full debugging support in 16 bit code, and limited debugging support in 32 bit code.
   BUILD_CODE = d ;
   CCOPTS   = -g     $(COMMON_CCOPTS) $(VERSION) -DDEBUG ;
   CC32OPTS = -g -O3 $(COMMON_CCOPTS) -DNDEBUG ;
   
} else if ( $(CURFLAV) == extra_debug ) {

   # full debugging support in all code. Can be pretty slow. Also making 32-bit code
   # debug may cause cache memory to overflow.
   BUILD_CODE = x ;
   CCOPTS   = -g     $(COMMON_CCOPTS) $(VERSION) -DDEBUG ;
   CC32OPTS = -g     $(COMMON_CCOPTS) -DDEBUG ;
   
} else {

   exit "CURFLAV must be set to one of extra_debug, debug, optimized, or release." ;
}

# Note: some systems can be built with paranoid checking or in debugging mode
# that makes them super slow but will catch some hard problems. Look in the code.

ASFLAGS = -mthumb-interwork -gstabs ;

CONV_FLAGS = -p $(CONV_DIR)/converters -p $(CONV_DIR)/template "-v $(VERBOSITY)" ;

C_HDRPATTERN      = "#[\t ]*include[\t ]*[\"]([^\"]*)[\"]" ;
S_HDRPATTERN      = ".include[\t ]*[\"]([^\"]*)[\"]" ;
DL_HDRPATTERN     = "file[\t ]*=[\t ]*[\"]([^\"]*)[\"]" ;

HDRSEARCH         = $(INCLUDE_DIRS) ;

SEARCH            = $(SRC_DIRS) $(INCLUDE_DIRS) $(LIB_DIRS) ;
SEARCH on "clean" = . ;

notfile all clean clean_data ;
always clean clean_data ;

# if you are using a Dos shell, these temporary files get created and must be deleted
# CLEAN_LIST = .output $(OBJ_DIR)static/.output $(OBJ_DIR)shared/.output ;
CLEAN_LIST = ;

depends all : $(ALL_TARGETS) ;

# rules --------------------------------------------

rule Library TARGET : SOURCES : LIBS : TARGET_DIR
{
   ObjectSet $(TARGET) : $(SOURCES) : $(LIBS) : LinkLibrary ;
}

rule Executable TARGET : SOURCES : LIBS : TARGET_DIR
{
   ObjectSet $(TARGET) : $(SOURCES) : $(LIBS) : LinkExecutable ;
}

rule ObjectSet TARGET : SOURCES : LIBS : LINK_RULE : TARGET_DIR
{
   LOCATE on $(TARGET) = $(TARGET_DIR) ;
   
   if ( $(TARGET_DIR) ) TARGET_DIR = $(TARGET_DIR)/ ;
   else                 TARGET_DIR = "" ;
   
   # divide up the sources and objects
   
   local OBJECTS      = $(SOURCES:I="\.o$") ;
   local C_SOURCES    = $(SOURCES:I="\.c$") ;
   local S_SOURCES    = $(SOURCES:I="\.s$") ;
   local DL_SOURCES   = $(SOURCES:I="\.dl$") ; 
   local PROJ_SOURCES = $(SOURCES:I="\.proj$") ; 
   local C16_SOURCES  = $(C_SOURCES:X="32\.c$") ;
   local C32_SOURCES  = $(C_SOURCES:I="32\.c$") ;
   local S16_SOURCES  = $(S_SOURCES:X="32\.s$") ;
   local S32_SOURCES  = $(S_SOURCES:I="32\.s$") ;
   
   HDRSCAN on $(C_SOURCES)  = $(C_HDRPATTERN)  ;
   HDRRULE on $(C_SOURCES)  = C_HeaderRule ;
   HDRSIZE on $(C_SOURCES)  = "2048" ;                  # some generated .c files are very large, so we scan only the first two k.

   HDRSCAN on $(S_SOURCES)  = $(S_HDRPATTERN)  ;
   HDRRULE on $(S_SOURCES)  = S_HeaderRule ;
   
   HDRSCAN on $(DL_SOURCES) = $(DL_HDRPATTERN) ;
   HDRRULE on $(DL_SOURCES) = DL_HeaderRule ;
   
   # for sources with a grist, we search a subdir of that name:
   for ( I in $(SOURCES) )
   {
      if ( $(I:G) )
      {
         SEARCH           on $(I) += $(SEARCH)/$(I:G) ;
         HDRSEARCH        on $(I) += $(SEARCH) ;
      }
   }   
   
   # compute dependencies
   for ( SRC_FILE in $(DL_SOURCES) )
   {
      C16_SOURCES += [ DlRule $(SRC_FILE:S=.c) : $(SRC_FILE) ] ;
      BRS_SPEC = $(DL_SOURCES:FD)/*.brs ;
      CLEAN_DATA_LIST += $(BRS_SPEC:M) ;
   }
   
   for ( SRC_FILE in $(PROJ_SOURCES) )
      OBJECTS += [ ProjRule $(SRC_FILE:S=.o) : $(SRC_FILE) ] ;
   
   for ( SRC_FILE in $(C16_SOURCES) )
      OBJECTS += [ Compile16Rule $(SRC_FILE:S=.o) : $(SRC_FILE) ] ;
   
   for ( SRC_FILE in $(C32_SOURCES) )
      OBJECTS += [ Compile32Rule $(SRC_FILE:N="32\.c$"0=".o32") : $(SRC_FILE) ] ;
   
   for ( SRC_FILE in $(S16_SOURCES) )
      OBJECTS += [ AssembleRule $(SRC_FILE:S=_asm.o) : $(SRC_FILE) ] ;
   
   for ( SRC_FILE in $(S32_SOURCES) )
      OBJECTS += [ AssembleRule $(SRC_FILE:S=_asm.o) : $(SRC_FILE) ] ;
      
   # bind localized objects explicitly
   for ( I in $(OBJECTS) )
   {
      if ( $(I:G) )
      {
         TMP = $(I:F="$(OBJ_DIR)$(BUILD_CODE)/$(I:G)_$(I:H)") ;
         
         INCLUDE_DIRS     on $(I) += $(INCLUDE_DIRS)/$(I:G) ;
         REL_INCLUDE_DIRS on $(I) += $(REL_INCLUDE_DIRS)/$(I:G) ;
         ABS_INCLUDE_DIRS on $(I) += $(ABS_INCLUDE_DIRS)/$(I:G) ;
      }
   }   
   
   LOCATE on $(OBJECTS) = $(OBJ_DIR)$(BUILD_CODE) ;
   depends $(TARGET) : $(OBJECTS) $(LIBS) ;
   temporary $(OBJECTS) ;
   
   CLEAN_LIST += $(TARGET) ;
   
   $(LINK_RULE) $(TARGET) : $(OBJECTS) $(LIBS) ;
}

rule DlRule C_SOURCE : DL_SOURCES
{
   # This is rather complex. Making this work in normal cases is easy. But getting this all 
   # to work when a subset of the files is missing ( like just the .h file or just the .c file etc. )
   # can be quite tricky
   
   # The .dl file generates a .h and .c file. The file must be 
   # generated in the same directory as the dl file. So 'locate' the .c and .h file in
   # the bound directory of the .dl file. This causes the .dl file to be bound earlier than
   # normal. Which in this case is ok.
   H_SOURCE = $(C_SOURCE:S=.h) ;
   LOCATE on $(C_SOURCE) $(H_SOURCE) = $(DL_SOURCES:FD) ;
   
   # both the .c and .h file depent on the .dl source. So if the .dl source changes both files will 
   # need updating. It is not normal for the .c and .h files to be dependent on anything. But its true 
   # in this case
   depends $(C_SOURCE) $(H_SOURCE) : $(DL_SOURCES) ;
   
   # Both files are temporary intermediate files, but we only mark the .c file as such,
   # Marking a file which is only included as a temporary file makes the time stamp calculation code
   # defer to the timestamp of the .dl file. If the dl file is current, the .h file will not be 
   # regenerated, even though it might be needed for some .c file which needs to be compiled.
   # temporary $(C_SOURCE) ;
   
   # normally the auto header scanning code would catch that the .c file includes the .h file, but not
   # in this case, because it is possible that the .c file has not yet been generated. 
   # But this is in fact a good thing. Without this dependency they both get built at the same time - as they
   # should. Adding this dependency will cause the build action to run twice.
   # includes $(C_SOURCE) : $(H_SOURCE) ;
   
   # The .o file generated by the .c file depends on the .h as well. Again this is not normal. The normal
   # case is that the .0 file depends on just the .c and the .c includes the .h. We need the .o to depend
   # on the .h because of the strange case where the .h is missing, but the .c exists. Without this 
   # hilarity would ensue.
   depends $(C_SOURCE:S=.o) : $(H_SOURCE) ;
   
   # now we call the .dl action. This action has two targets which is also not normal.
   ProcessDl $(C_SOURCE) $(H_SOURCE) : $(DL_SOURCES) ;
   
   CLEAN_DATA_LIST += $(C_SOURCE) $(H_SOURCE) ;
   return $(C_SOURCE) ; 
}

rule ProjRule OBJECT : PROJ_SOURCE
{
   depends $(OBJECT) : $(PROJ_SOURCE) ;
   ProcessProj $(OBJECT) : $(PROJ_SOURCE) ;
   CLEAN_LIST += $(OBJECT) ;
   
   PROJ_NAMES_C = $(PROJ_SOURCE:B)_names.c ; 
   PROJ_NAMES_H = $(PROJ_SOURCE:B)_names.h ;
   PROJ_NAMES = $(PROJ_NAMES_C) $(PROJ_NAMES_H) ;
   LOCATE on $(PROJ_NAMES) = $(PROJ_SOURCE:FD) ;
   depends $(PROJ_NAMES) : $(PROJ_SOURCE:S=.h) ;
   depends $(PROJ_NAMES:S=.o) : $(PROJ_NAMES_H) ;
   depends $(PROJ_NAMES_C) : $(PROJ_NAMES_H) ;
   
   CLEAN_DATA_LIST += $(PROJ_NAMES) ;
   
   ProcessProjNames $(PROJ_NAMES) : $(PROJ_SOURCE:S=.h) ;
   
   return $(OBJECT) ;
}

rule Compile16Rule OBJECT : C_SOURCE
{
   depends $(OBJECT) : $(C_SOURCE) ;
   CompileObject16 $(OBJECT) : $(C_SOURCE) ;
   CLEAN_LIST += $(OBJECT) ;
   return $(OBJECT) ;
}

rule Compile32Rule OBJECT : C_SOURCE
{
   depends $(OBJECT) : $(C_SOURCE) ;
   CompileObject32 $(OBJECT) : $(C_SOURCE) ;
   CLEAN_LIST += $(OBJECT) ;
   return $(OBJECT) ;
}

rule AssembleRule OBJECT : S_SOURCE
{
   depends $(OBJECT) : $(S_SOURCE) ;
   AssembleObject $(OBJECT) : $(S_SOURCE) ;
   CLEAN_LIST += $(OBJECT) ;
   return $(OBJECT) ;
}

rule C_HeaderRule INCLUDER : INCLUDEDS
{
   includes $(INCLUDER) : $(INCLUDEDS) ;        
   HDRSCAN on $(INCLUDEDS) = $(C_HDRPATTERN) ; # recursively search headers
   HDRRULE on $(INCLUDEDS) = C_HeaderRule ;
   nocare $(INCLUDEDS) ;
}

rule S_HeaderRule INCLUDER : INCLUDEDS
{
   includes $(INCLUDER) : $(INCLUDEDS) ;        
   HDRSCAN on $(INCLUDEDS) = $(S_HDRPATTERN) ; # recursively search headers
   HDRRULE on $(INCLUDEDS) = S_HeaderRule ;
   nocare $(INCLUDEDS) ;
}

rule BrushRule BRUSH_FILES : DL_FILE
{
   BRUSH_DIR = $(DL_FILE:FD) ;
   LOCATE on $(BRUSH_FILES) = $(BRUSH_DIR) ;
   
   BRUSH_IMAGES = $(BRUSH_DIR)/brush/*/*.bmp $(BRUSH_DIR)/brush/*/*.png ;
   BRUSH_IMAGES = $(BRUSH_IMAGES:M) ;
   depends $(BRUSH_FILES) : $(BRUSH_IMAGES) ;
      
#   CLEAN_DATA_LIST += $(BRUSH_FILES) ;

   {
      SCRIPT = make_brushes.txt ;
      local UNMATCHED_BRUSHES ;
      
      while ( $(BRUSH_FILES) )
      {
         local FILE = $(BRUSH_FILES[0]) ;
         
         LSCRIPT = $(FILE:S=.txt) ;
         if ( $(LSCRIPT:M) )
         {
            ConvertBrush $(FILE) : $(LSCRIPT) ;
         } else {
            UNMATCHED_BRUSHES += $(FILE) ;
         }
         
         BRUSH_FILES = $(BRUSH_FILES[1-]) ;
      }
      
      if ( $(UNMATCHED_BRUSHES) )
         ConvertBrush $(UNMATCHED_BRUSHES) : $(SCRIPT) ;
   }
}

rule DL_HeaderRule INCLUDER : INCLUDEDS
{
   BRUSH_FILES = $(INCLUDEDS:I="\.brs$") ;
   IMAGE_FILES = $(INCLUDEDS:X="\.brs$") ;

   includes $(INCLUDER) : $(INCLUDEDS) ;
   
   BrushRule $(BRUSH_FILES) : $(INCLUDER) ; 
}

rule CleanRule TARGET : GENERATED_FILES : TARGET_DIR
{
   local LIST = $(TARGET) ;        # all the targets
   
   if ( $(TARGET_DIR) ) CLEAN_LIST += $(TARGET_DIR)$(TARGETS) ;
   else                 CLEAN_LIST += $(TARGETS) ;
   
   CLEAN_LIST += $(GENERATED_FILES) ;
}

# actions --------------------------------------------

action quietly CompileObject16 TARGET : SOURCE
{%
   echo Compiling  [$(BUILD_CODE)] $(TARGET:H) : $(SOURCE:H)
   $(CC) $(CCOPTS) -I$(REL_INCLUDE_DIRS) -I$(ABS_INCLUDE_DIRS) $(SOURCE) -o $(TARGET) $(SILENCE)
%}

action quietly CompileObject32 TARGET : SOURCE
{%
   echo Compiling  [$(BUILD_CODE)] $(TARGET:H) : $(SOURCE:H)
   $(CC) $(CC32OPTS) -I$(REL_INCLUDE_DIRS) -I$(ABS_INCLUDE_DIRS) $(SOURCE) -o $(TARGET) $(SILENCE)
%}

action quietly AssembleObject TARGET : SOURCE
{%
   echo Assembling [$(BUILD_CODE)] $(TARGET:H) : $(SOURCE:H)
   $(AS) $(ASFLAGS) -I$(REL_INCLUDE_DIRS) -I$(ABS_INCLUDE_DIRS) -o $(TARGET) $(SOURCE) $(SILENCE)
   $(PROCESS_SILENCE)
%}

action quietly LinkLibrary TARGET : SOURCES
{%
   echo Linking        $(TARGET:H) : $(SOURCES:H)
   $(LINKER) -rs $(TARGET) $(SOURCES) $(SILENCE)
%}

action quietly LinkExecutable TARGET : SOURCES
{%
   echo Linking        $(TARGET:H) : $(SOURCES:H) 
   $(LINKER) -rs $(TARGET) $(SOURCES) $(SILENCE)
%}

action piecemeal existing quietly Clean TARGET : SOURCES
{%
   echo Cleaning       $(SOURCES)
   rm -f $(SOURCES:C)
%}

action quietly ProcessDl TARGET : SOURCES
{%
   echo Convert        $(TARGET:H) : $(SOURCES:H)
   $(CONV) $(CONV_FLAGS) -d$(SOURCES) -w $(TARGET[1]:D) -o $(TARGET[1]:B)
%}

action quietly ProcessProj TARGET : SOURCE
{%
   echo Convert        $(TARGET:H) : $(SOURCE:H)
   $(OBJCOPY) -v -I binary --add-section .music=$(SOURCE) --remove-section=.data -O elf32-little $(SOURCE) $(TARGET) | echo
%}

action quietly ProcessProjNames TARGET : SOURCE
{%
   echo Convert        $(TARGET:H) : $(SOURCE:H)
   $(PERL) $(GBA_DIR)/musyx/extract_names.pl   < $(SOURCE) > $(TARGET[0])
   $(PERL) $(GBA_DIR)/musyx/extract_names_h.pl < $(SOURCE) > $(TARGET[1])
%}

action quietly ConvertBrush TARGETS : SCRIPT
{%
   echo ConvertBrush   $(TARGETS:H) : $(SCRIPT)
   $(BRSEDIT) -s -p brush -w $(TARGETS[0]:D) -i $(SCRIPT:H)
%}

The following is the jamfile which the user would deal with, listing off all the source files, and relevant directories. This is for a project with a total of over 1600 target files.


#
# rayman4: an isometric rayman game
#

GBA_DIR      = .. ;
TOOLS_DIR    = $(GBA_DIR)/../tools ;

INCLUDE_DIRS = src src/* src/*/src data/* $(GBA_DIR)/milk/systems 
               $(GBA_DIR)/milk/services $(GBA_DIR)/milk/lib $(GBA_DIR)/milk/lib/tool_font 
               $(GBA_DIR)/gba/include $(GBA_DIR)/gba/include/backup $(GBA_DIR)/musyx ;
LIB_DIRS     = $(GBA_DIR)/gba/lib $(GBA_DIR)/milk ;
SRC_DIRS     = . src src/* src/*/src data/*/* data/anim/machine/* 
               $(GBA_DIR)/milk/systems/* $(GBA_DIR)/milk/services/* ;
ALL_TARGETS  = rayman_na rayman_eu ;

include "$(GBA_DIR)/scripts/jambase" ;
include "$(GBA_DIR)/milk/jam_milksrc" ;

depends rayman    : rayman_na ;
depends rayman_eu : rayman4_na.bin ;
depends rayman_na : rayman4_eu.bin ;
depends serv      : serv_na ;
depends serv_na   : libmilkserv_na.a ;
depends serv_eu   : libmilkserv_eu.a ;
depends sys       : libmilksys.a ;

notfile rayman rayman_na rayman_eu serv serv_na serv_eu sys ;

AI_SOURCES = iso_begoniax.c iso_bego_spell.c iso_button.c iso_crab.c                  
    iso_cutscene.c iso_effect.c iso_firemonster.c iso_flyer.c
    iso_gate.c iso_gem.c iso_globox.c iso_globox_barrel.c
    iso_hoodbomber.c iso_hoodbullet.c iso_hoodcaptain.c 
    iso_hoodgrenade.c iso_hoodsoldier.c iso_knight.c iso_machine.c
    iso_menhir.c iso_pedestal.c iso_piranha.c iso_powerup.c
    iso_prop.c iso_raft.c iso_rayman.c iso_rayfist.c iso_reflux.c
    iso_reflux_barrel.c iso_reflux_orb.c iso_reflux_wave.c iso_rune.c
    iso_slapdash.c iso_spark.c iso_spiker.c iso_switch.c 
    iso_teensie_cage.c iso_teensy.c iso_toad.c ;

FRONTEND_SOURCES = lang_ctx.c menu_ctx.c credits_ctx.c sound_ctx.c 
    menu_ctx32.c splash_ctx.c score_ctx.c name_ctx.c
    erase_ctx.c reset_save_ctx.c seek_interpolate.c ;

PROG_SOURCES = $(AI_SOURCES) $(FRONTEND_SOURCES)
    ai_group.c ai_console.c ai_update.c boot_ctx.c camera.c camera_update.c 
    circle_transition.c cutscene.c dialog.c slideshow.c dr_math.c game_over.c
    gamestate.c hoodlum_util.c igmenu.c igui.c iso_camera.c iso_camera32.c 
    iso_collision.c iso_collision32.c iso_levels.c iso_physics.c iso_physics32.c
    iso_util.c iso_checkpoint.c isometric.c level_end.c level_score_ctx.c
    list.c main.c map_select.c obj_collision.c obj_collision32.c pal_fx.c
    seek.c shadow.c wipe_transition.c worldobject.c text.c ;

DATA_SOURCES = rayman_font.dl igui_data.dl worldobjects.dl rayman_anims.dl
    prop_anims.dl pup_anims.dl globox_anims.dl hoodlum_bomber_anims.dl 
    hoodlum_captain_anims.dl hoodlum_soldier_anims.dl flyer_anims.dl
    slapdash_anims.dl knight_anims.dl crab_anims.dl piranha_anims.dl
    spiker_anims.dl teensy_anims.dl machine_anims.dl raft_anims.dl 
    cutscene_anims.dl splash.dl minimap.dl title_data.dl rayman_music.proj
    pausemenu.dl begoniax_anims.dl toad_anims.dl firemonster_anims.dl 
    clone_anims.dl ;

LEVEL_SOURCES = levels.dl levels2.dl levels3.dl ;
 
TEXT_SOURCES = text.dl dialog_text.dl tutorial_text.dl teensies_text.dl ;

# We need to manually declare depenencies for generated headers
depends levels.h levels1.h level2.h : text.h splash.h ;

DlRule  text.c  : $(TEXT_SOURCES) ;

Library libmilkserv_eu.a  : $(MILK_SERV_SOURCES) boot.s $(MILK_SERV_DATA) ;
Library libmilkserv_na.a  : $(MILK_SERV_SOURCES) boot.s $(MILK_SERV_DATA) ;
Library libmilksys.a      : $(MILK_SYS_SOURCES)                               ;

Executable rayman4_na.bin : $(PROG_SOURCES) $(DATA_SOURCES) $(LEVEL_SOURCES) : libmilkserv_na.a libmilksys.a ;
Executable rayman4_eu.bin : $(PROG_SOURCES) $(DATA_SOURCES) $(LEVEL_SOURCES) : libmilkserv_eu.a libmilksys.a ;


Clean clean      : $(CLEAN_LIST) ;
Clean clean_data : $(CLEAN_DATA_LIST) ;