lpbuild: universal Makefile updated

git-svn-id: svn://ultimatepp.org/upp/trunk@2630 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
dolik 2010-08-20 23:10:09 +00:00
parent b49390bc45
commit ff613ca996

View file

@ -5,7 +5,7 @@
# \____/ (____)(__) (__)(_/\/\_)(_)(_)(__) (___) (_) (_) #
# ___ ___ __ __ __ ___ _ _ __ ___ _ _ #
# ( _)( ,) ( ) ( \/ )( _)( \/\/ )/ \ ( ,) ( )/ ) #
# ) _) ) \ /__\ ) ( ) _) \ /( () ) ) \ )  \ #
# ) _) ) \ /__\ ) ( ) _) \ /( () ) ) \ ) \ #
# (_) (_)\_)(_)(_)(_/\/\_)(___) \/\/ \__/ (_)\_)(_)\_) #
# __ __ __ _ _ ___ ___ __ __ ___ #
# ( \/ ) ( ) ( )/ )( _)( _)( )( ) ( _) #
@ -16,15 +16,15 @@
############################## USAGE ###################################
# This makefile is controlled through a set of variables, similar to #
# any other makefile. The preffered way to change them is via command #
# any other makefile. The prefered way to change them is via command #
# line, e.g.: make "PKG=ide usvn" CC=g++ #
# #
# All of the variables that can control the bahaviour are listed below #
# in DEFAULTS section. Also their default value is shown here. #
# #
# For boolean type of variables, values of y, Y, yes, YES, Yes, true, #
# True, T, t, 1, on, On, ON and @ are considered true, anything else #
# is evaluated as false. #
# True, TRUE, T, t, 1, on, On, ON and @ are considered true. Anything #
# else is evaluated as false. #
############################# TARGETS ##################################
# This makefile defines four targets that are meant to be used by user #
# from the command line: #
@ -33,30 +33,46 @@
# help - shows this help (default if no packages are selected) #
# clean - deletes OBJDIR #
# export - exports preparsed Makefile to file EXPORT #
############################# EXAMPLES #################################
# Typical usage: #
# make PKG=ide FLAGS="GUI MT" #
# More complicated usage: #
# make PKG="ide UWord Bombs" FLAGS="GUI GCC NOGTK" VERBOSE=y #
# Building "theide" instead of "ide": #
# make PKG=ide BINPREFIX=bin/the #
# Export makefile with full dependencies: #
# make export PKG=ide FAST=n #
# Show help: #
# make OR make help #
# Simulation mode: #
# make PKG=ide SIMULATE=y #
# Silent mode: #
# make PKG=ide SILENT=y #
# Paralel compilation (3 paralel processes): #
# make PKG=ide JOBS=3 #
############################# DEFAULTS #################################
# List of packages to build (space separated) #
# List of packages to build (space separated). #
# If empty, this help text will be shown. #
PKG=
# Paths where to look for packages (in U++ terminology DIRS=assembly #
# and each path is a package nest) #
DIRS=bazaar examples reference tutorial uppsrc
# Paths where to look for packages. #
NESTS=bazaar examples reference tutorial uppsrc
# Flags as in TheIDE.
# The only difference is, that if you use SPEED or SIZE flag, then #
# the application will be optimized for Speed or Size, respectively. #
# Flags STATIC and ALLSHARED will determine the linking mode. #
FLAGS=GCC
# Additional include paths for compiler (without leading "-I") #
# Additional include paths for compiler (without leading "-I"). #
INCPATHS:=
# Paths to libraries for linker #
# Paths to libraries for linker. #
LIBPATHS:=
# Directory to store intermediate object files #
OBJDIR:= _out
# Extension added to resulting executables #
BINEXT:=.out
BINEXT:=
# Prefix for resulting binaries. Usually used with "/" at the end, #
# to specify the directory (hence the name) #
BINDIR:=
# ar command #
BINPREFIX:=bin/
# ar command #
AR:= ar -src
# C compiler command #
CC:= c++
@ -83,18 +99,20 @@ VERBOSE:=
SIMULATE:=
# Do not make full dependency scan (using gcc -M switch). #
# This saves a lot of time and usually doesn't make any harm. #
# FAST mode must by turned of only if you modified include files (*.h, #
# FAST mode must by turned on only if you modified include files (*.h, #
# *.lay, ...) without modifying the *.c/cpp files that include them. #
FAST:=y
# Add flags from first line of mainconfig section #
# Add flags from based on mainconfig section (not implemented yet) #
USEMAINCFG:=
# Number of parallel jobs to use (like "make -jN" option). #
# Please, use JOBS when building multiple packages rather than -j, as #
# the later leads cluttered output and often also to performance loss. #
# When building a single package, this option is disregarded and you #
# should use -j option. #
# Empty value means single job (i.e. serial processing). #
JOBS:=
# The JOBS variable must be used instead of -j option (as -j would not #
# work as expected). Default value is the number of cores (if it is #
# possible to detect) or 1. #
JOBS:=$(shell echo /sys/devices/system/cpu/cpu[0-9] | wc -w || echo 1)
# External parser to use instead of the contained one. Also used #
# for manipulation with the internal parser (see update-parser, #
# and export-parser targets in this Makefile for details). #
PARSER:=
# Exported makefile (used only with export target) #
# If multiple packages are being exported, each produces additional #
# Makefile, which name is EXPORT.package_name #
@ -102,18 +120,13 @@ EXPORT:=Makefile.export
# Awk binary. Used only for export. #
AWK:=awk
########################################################################
# HELP END
ifeq ($(filter 3.8%, $(MAKE_VERSION)), )
$(error This Makefile only supports GNU make version 3.80 and newer)
endif
# if only one package is given, we can parse and compile,
# otherwise we call make on each given package
ifeq ($(words $(PKG)),1)
single-package:=y
endif
# no need to parse dependencies just to delete a directory
# no need to parse dependencies just to clean up or to read help etc.
ifeq ($(MAKECMDGOALS),clean)
skip-parse:=y
endif
@ -127,387 +140,414 @@ ifeq ($(PKG),)
skip-parse:=y
endif
# Return last word of argument
lastword=$(word $(words $1),$1)
# Determine the name of this Makefile
filename:=$(call lastword,$(MAKEFILE_LIST))
filename:=$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
# Helper functions to put variables on cmdline
pass-var= "$1=$$($1)"
export-var= "$1=$($1)"
# list of user variables to pass to child processes
cmdline-vars=DIRS FLAGS INCPATHS LIBPATHS OBJDIR BINEXT BINDIR AR CC CXX\
cmdline-vars=NESTS FLAGS INCPATHS LIBPATHS OBJDIR BINEXT BINPREFIX AR CC CXX\
CFLAGS CXXFLAGS LDFLAGS SPEEDFLAGS SIZEFLAGS PLATFORM\
SILENT VERBOSE SIMULATE FAST USEMAINCFG
CONFIRM=y Y yes YES Yes true True T t 1 on On ON @
L:=(
R:=)
CONFIRM=y Y yes YES Yes true True T TRUE t 1 on On ON @
confirm=$(filter $(strip $1),$(CONFIRM))
# verbose/silent/fast mode switches
S:=$(call confirm,$(SILENT))
M:=$(if $(call confirm,$(VERBOSE)),,@)
M:=$(if $(call confirm,$(SIMULATE)),@echo SIMULATE: ,$M)
F:=$(call confirm,$(FAST))
# echo a string if silent mode is not on
echo=$(if $S,,@echo $1)
ifneq ($(skip-parse),y)
CINC:=$(foreach d,$(DIRS) $(INCPATHS), -I$d) -I/usr/include/freetype2 -I/usr/include/gtk-2.0 -I/usr/local/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/local/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/local/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include -I/usr/local/lib/gtk-2.0/include -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/include/glib-2.0 -I/usr/X11R6/lib/glib-2.0/include -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/cairo -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include/atk-1.0 -I/usr/local/include/cairo -I/usr/local/include/pango-1.0 -I/usr/local/include/atk-1.0 -I/usr/local/include -I/usr/local/include/libpng
flags:=$(sort $(FLAGS) $(PLATFORM))
CINC:=$(foreach d,$(NESTS) $(INCPATHS), -I$d) -I/usr/include/freetype2 -I/usr/include/gtk-2.0 -I/usr/local/include/gtk-2.0 -I/usr/include/glib-2.0 -I/usr/local/include/glib-2.0 -I/usr/lib/glib-2.0/include -I/usr/local/lib/glib-2.0/include -I/usr/lib/gtk-2.0/include -I/usr/local/lib/gtk-2.0/include -I/usr/include/cairo -I/usr/include/pango-1.0 -I/usr/include/atk-1.0 -I/usr/X11R6/include -I/usr/X11R6/include/freetype2 -I/usr/X11R6/include/gtk-2.0 -I/usr/X11R6/include/glib-2.0 -I/usr/X11R6/lib/glib-2.0/include -I/usr/X11R6/lib/gtk-2.0/include -I/usr/X11R6/include/cairo -I/usr/X11R6/include/pango-1.0 -I/usr/X11R6/include/atk-1.0 -I/usr/local/include/cairo -I/usr/local/include/pango-1.0 -I/usr/local/include/atk-1.0 -I/usr/local/include -I/usr/local/include/libpng
LIBPATH:=$(foreach d,$(LIBPATHS), -L$d) -L/usr/X11R6/lib -L/usr/lib -L/usr/local/lib
needed-dirs:=$(BINDIR)
needed-dirs:=$(dir $(BINPREFIX))
# UPP FILES PARSER
# read whole file to hold space end execute everything on the last line (multiline matching)
header:=1h;1!H;$${
footer:=}
# helpers:
s:=\t \x0A\x0D
del-section=g;s/;[$s]*$1[^;]*;/;/g;s/^$1[^;]*;/;/g;h;
sel:=s/^[^@]*@</@</; s/>@[^@]*$$/>@/; s/>@[^@]*@</>@\n@</g;
slash:=s/\\/\//g;
# PREPARSE:
# change (..(..)..) to (..[..]..)
p1:=g;:a;s/(\([^\n]*\)(\([^()]*\))\([^\n]*\))/(\1[\2]\3)/;t a;
# change (...) to ([...])
p2:=s/(\([^\n]*\))/([\1])/g;
# delete spaces on left ...
p3:=:b;s/\[\([^$s]*\)[$s][$s]*\([&|]\)\([^\n]*\)\]/[\1\2\3]/;
# ... and right side of operator
p4:=s/\[\([^$s]*\)\([&|]\)[$s][$s]*\([^\n]*\)\]/[\1\2\3]/;t b;
# insert implicit &
p5:=:c;s/\[\([a-zA-Z0-9_!&|]*\)[$s][$s]*\([^\n]*\)\]/[\1\&\2]/;t c;
# place {} around flags
p6:=s/\([![&|]\)\([a-zA-Z0-9_][a-zA-Z0-9_]*\)/\1{\2}/g;
# save preparsed to hold space
p7:=h;
# EVALUATION
# filter only library clauses
e1:=$(sel)
# evalualte flags
e2:=s/{\([^{}]*\)}/{$$(call has-flag,\1,$$1)}/g;
# fix unconditional cases
e3:=s/@<\([^()@]*\)>@/@<({T}) \1>@/g;
# evaluate negations
e4:=s/!{\([^{}]*\)}/{$$(if \1,,T)}/g;
# evaluate ANDs
e5:=s/{\([^{}]*\)}&{\([^{}]*\)}/{$$(call and,\1,\2)}/g;
# evaluate ORs
e6:=s/{\([^{}]*\)}|{\([^{}]*\)}/{$$(call or,\1,\2)}/g;
# remove [] around already evluated statements
e7:=s/\[\({[^{}]*}\)\]/\1/g;
# PARSER:
# mark contents of library clauses with @<..>@
u1=:u1$1;s/;[$s]*$1\([$s(][^;]*\);/;@<\1>@;/;
u1+=s/^$1\([$s(][^;]*\);/@<\1>@;/;t u1$1;
# delete commas, newlines and unnecessary spaces
u2:=s/[$s,][$s,]*/ /g;
# remove quotations, we do not need them
u3:=s/"\([^"@]*\)"/\1/g;
# evaluate boolean expressions
u4=$(e1)$(e2)$(e3):$1;$(e4)$(e5)$(e6)$(e7)t $1;
# print final evals
u5=$(slash)s/@<({\([^{}]*\)})\([^@]*\)>@/$$(eval $$1_$1+=$$(if \1,\2,))/gp;
# function that returns full parser
get-key=g;$(call u1,$1)$(u2)$(u3)$(call u4,$1)$(call u5,$2)$(call del-section,$1)
# Specialized parsers follow:
# DESCRIPTION: (note: last description found in file is used)
d1=g;s/;[$s]*description[$s]*"\([^\n]*\)".*/$$(eval $$1_DESC:=\1 )/p;
d1+=s/^description[$s]*"\([^\n]*\)".*/$$(eval $$1_DESC:=\1 )/p;
# FILE:
# mark contents of files clauses with @<..>@
f1=g;s/;[$s]*file[$s]*\([^;]*\);/@<,\1,>@/g;
f1+=s/^[$s]*file[$s]*\([^;]*\);/@<,\1,>@/g;
# filter only files clauses
f2:=$(sel)
# remove separators
f3:=s/,[$s]*[^,]*separator[$s]*,/,/g;
# file.cpp optimize_size -> file.cpp_size
f4:=s/,[$s]*\([^$s,]*\)[$s]*optimize_size/,\n\1_size/g;
# file.cpp optimize_speed -> file.cpp_speed
f5:=s/,[$s]*\([^$s,]*\)[$s]*optimize_speed/,\n\1_speed/g;
# drop modificators (charset, readonly, highlight, tabsize, font, ...)
f6:=s/,[$s]*\([^$s,@][^$s,@]*\)[^,@]*/,\n\1/g;
# filter only files with extension (to remove licenses etc.)
f7:=s/,[$s]*\([^.,@]*\)[$s]*,/,/g;
# delete commas, newlines, unnecessary spaces and quotation marks
f8:=s/[$s,][$s,]*/ /g; s/"//g;
# write final eval to output
f9:=$(slash)s/@<[$s,]*\([^@]*\)[$s,]*>@/$$(eval $$1_FILES+= \1)/gp;
# ACCEPT
# mark contents of files clauses with @<..>@
a1:=g;s/;[$s]*acceptflags\([$s][^;]*\);/@<,\1,>@/g;
# filter only files clauses
a2:=$(sel)
# delete commas, newlines, unnecessary spaces and quotation marks
a3:=s/[$s,][$s,]*/ /g; s/"//g;
# write final eval to output
a4:=s/@<[$s,]*\([^@]*\)[$s,]*>@/$$(eval $$1_ACCEPT+= \1)/gp;
# OPTIMIZE SPEED/SIZE:
o1=g;s/.*;[$s]*optimize_speed[$s]*;.*/$$(eval $$1_SPEED:=TRUE)/gp;
o2=g;s/.*;[$s]*optimize_size[$s]*;.*/$$(eval $$1_SIZE:=TRUE)/gp;
# MAINCONFIG:
m1:=g;s/;[$s]*mainconfig\([$s][^;]*\);/@<,\1,>@/g;
# filter only mainconfig clauses
m2:=$(sel)
# "a" = "b" -> "a":"b"
m3:=s/"[$s]*=[$s]*"/":"/g;
# replace spaces with underscores
m4:=:m;s/"\([^$s:,"][^$s:,"]*\)[$s][$s]*\([^":,]*\)"/"\1_\2"/g;t m;
# delete commas, newlines and unnecessary spaces
m5:=s/[$s,][$s,]*/ /g;
# drop quotations
m6:=s/"*\([^":]*\)"*:"*\(\([^":]*\)\)"*/\1:\2/g;
# write final eval to output
m7:=s/@<[$s,]*\([^@]*\)[$s,]*>@/$$(eval $$1_CFG+= \1)/gp;
# put it all together
PARSER:='$(header)\
$(d1)$(call del-section,description)\
$(f1)$(f2)$(f3)$(f4)$(f5)$(f6)$(f7)$(f8)$(f9)$(call del-section,file)\
$(a1)$(a2)$(a3)$(a4)$(call del-section,acceptflags)\
$(o1)$(o2)$(call del-section,optimize_speed)$(call del-section,optimize_size)\
$(m1)$(m2)$(m3)$(m4)$(m5)$(m6)$(m7)$(call del-section,mainconfig)\
$(p1)$(p2)$(p3)$(p4)$(p5)$(p6)$(p7)\
$(call get-key,flags,FLAGS)\
$(call get-key,library,LIBS)\
$(call get-key,uses,USES)\
$(call get-key,link,LINK)\
$(call get-key,options,OPTS)\
$(call get-key,include,INC)\
$(call get-key,target,TARG)\
$(footer)'
#Return true, if package $2 is to be compiled flag $1
has-flag=$(filter $1,$($2_FLAGS))
# Return true, if package $2 is to be compiled with flag $1
has-flag=$(filter $1,$2)
#Join array with "_"
concatenate=$(strip $(if $1,$(if $(filter $(words $1),1),$1,\
$(firstword $1)_$(call concatenate,$(wordlist 2,$(words $1),$1)))))
# return output directory for .o files, based on pkg name and flags
# Return output directory for .o files, based on pkg name and flags
objdir=$(OBJDIR)/$1/$(call concatenate,$($1_FLAGS))
# source files needed to compile package $1
get-src=$(filter %.cpp,$($1_FILES))\
$(filter %.icpp,$($1_FILES))\
$(filter %.c,$($1_FILES))
get-src-speed=$(patsubst %_speed,%,$(filter %_speed,$($1_FILES)))
get-src-size=$(patsubst %_size,%,$(filter %_size,$($1_FILES)))
# Find all files in DIRS
available-files:=$(sort \
$(foreach d,$(DIRS),\
$(shell find $d -name .svn -prune -o -type f -print)\
)\
)
available-upps:=$(filter %.upp,$(available-files))
assert-file=$(if $1,$1,$(error File $2 not found!))
# Find exact location of given .upp file
find-upp=$(call assert-file,$(filter $(foreach d,$(DIRS),$d/$1/$(notdir $1.upp)),$(available-upps)),$1/$1.upp)
find-file=$(call assert-file,$(filter $(foreach d,$(DIRS),$d/$1/$2),$(available-files)),$1/$2)
# add directory of file $2 to the list of required directories
needs-dir=needed-dirs+= $(dir $2)
# get dependencies recursively
get-deps=$(sort $1 $(foreach p,$($1_USES),$(call get-deps,$p)))
#TODO: improve
compiler=$(if $(filter-out .c,$(suffix $1)),\
$(CXX) -c -x c++ $(CXXFLAGS),\
$(CC) -c -x c $(CFLAGS))
analyze-info=$(if $S,, Analyzing $1 dependencies ...)
includes-fetcher=$(info $(analyze-info))$(compiler) -M
# Compiler commands
compiler-cpp=$(CXX) -c -x c++ $(CXXFLAGS)
compiler-c=$(CC) -c -x c $(CFLAGS)
analyze-info=$(if $S,, Analyzing $1 dependencies ...)
includes-fetcher-c=$(info $(analyze-info))$(CC) -M -x c $(CXXFLAGS)
includes-fetcher-cpp=$(info $(analyze-info))$(CC) -M -x c++ $(CFLAGS)
#compile .cpp into .o
# general target scheme (args:{deps,target,pkg,lang,flags})
define compile
$(eval $(needs-dir))
$(if $F,$2: $1,$(dir $2)$(shell $(includes-fetcher) $(CINC) $($3_MACRO) $1 |tr -d '\\'))
$(call echo," Compiling $1 with flags $($3_FLAGS) ...")
$M$(compiler) $(CINC) $($3_MACRO) $1 -o $2
$(ENDLINE)
$(if $F,$2: $($3_NEST)/$1,$(dir $2)$(shell $(includes-fetcher-$4) $(CINC) $($3_MACRO) $($3_NEST)/$1 |tr -d '\\\\'))
$(call echo," Compiling $1 $Lflags $(strip $($3_FLAGS))$R ...")
$M$(compiler-$4) $5 $(CINC) $($3_MACRO) $($3_NEST)/$1 -o $2
$(EMPTYLINE)
endef
#compile .cpp/.c/.icpp into .o/.io
compile-cpp=$(call compile,$1,$2,$3,cpp,)
compile-c=$(call compile,$1,$2,$3,c,)
compile-icpp=$(call compile,$1,$2,$3,cpp,)
# ditto with speed optimization
define compile-speed
$(eval $(needs-dir))
$(if $F,$2: $1,$(dir $2)$(shell $(includes-fetcher) $(SPEEDFLAGS) $(CINC) $($3_MACRO) $1 |tr -d "\\\\"))
$(call echo," Compiling $1 with flags $($3_FLAGS) and speed optimizations ...")
$M$(compiler) $(SPEEDFLAGS) $(CINC) $($3_MACRO) $1 -o $2
$(ENDLINE)
endef
compile-cpp-speed=$(call compile,$1,$2,$3,cpp,$(SPEEDFLAGS))
compile-c-speed=$(call compile,$1,$2,$3,c,$(SPEEDFLAGS))
compile-icpp-speed=$(call compile,$1,$2,$3,cpp,$(SPEEDFLAGS))
# ditto with size optimization
define compile-size
$(eval $(needs-dir))
$(if $F,$2: $1,$(dir $2)$(shell $(includes-fetcher) $(SIZEFLAGS) $(CINC) $($3_MACRO) $1 |tr -d "\\\\"))
$(call echo," Compiling $1 with flags $($3_FLAGS) and size optimizations ...")
$M $(compiler) $(SIZEFLAGS) $(CINC) $($3_MACRO) $1 -o $2
$(ENDLINE)
endef
compile-cpp-size=$(call compile,$1,$2,$3,cpp,$(SIZEFLAGS))
compile-c-size=$(call compile,$1,$2,$3,c,$(SIZEFLAGS))
compile-icpp-size=$(call compile,$1,$2,$3,cpp,$(SIZEFLAGS))
# call appropriate compile functions for all files
define compile-all
$(if $(call has-flag,SPEED,$1),\
$(foreach f,$($1_SRC),$(call compile-speed,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp),.i,).o,$1))\
$(foreach f,$($1_SRC_SPEED),$(call compile-speed,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_speed),.i,).o,$1))\
$(foreach f,$($1_SRC_SIZE),$(call compile-speed,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_size),.i,).o,$1)),\
$(if $(call has-flag,SIZE,$1),\
$(foreach f,$($1_SRC),$(call compile-size,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp),.i,).o,$1))\
$(foreach f,$($1_SRC_SPEED),$(call compile-size,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_speed),.i,).o,$1))\
$(foreach f,$($1_SRC_SIZE),$(call compile-size,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_size),.i,).o,$1))\
$(if $(call has-flag,SPEED,$($1_FLAGS)),\
$(foreach f,$($1_FILES_CPP) $($1_FILES_CPP_SPEED) $($1_FILES_CPP_SIZE),$(call compile-cpp-speed,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_C) $($1_FILES_C_SPEED) $($1_FILES_C_SIZE),$(call compile-c-speed,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_ICPP) $($1_FILES_ICPP_SPEED) $($1_FILES_ICPP_SIZE),$(call compile-icpp-speed,$1/$f,$($1_OBJDIR)/$(basename $f).io,$1))\
,$(if $(call has-flag,SIZE,$($1_FLAGS)),\
$(foreach f,$($1_FILES_CPP) $($1_FILES_CPP_SPEED) $($1_FILES_CPP_SIZE),$(call compile-cpp-size,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_C) $($1_FILES_C_SPEED) $($1_FILES_C_SIZE),$(call compile-c-size,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_ICPP) $($1_FILES_ICPP_SPEED) $($1_FILES_ICPP_SIZE),$(call compile-icpp-size,$1/$f,$($1_OBJDIR)/$(basename $f).io,$1))\
,\
$(foreach f,$($1_SRC),$(call compile,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp),.i,).o,$1))\
$(foreach f,$($1_SRC_SPEED),$(call compile-speed,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_speed),.i,).o,$1))\
$(foreach f,$($1_SRC_SIZE),$(call compile-size,$(call find-file,$1,$f),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_size),.i,).o,$1))\
$(foreach f,$($1_FILES_CPP),$(call compile-cpp,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_C),$(call compile-c,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_ICPP),$(call compile-icpp,$1/$f,$($1_OBJDIR)/$(basename $f).io,$1))\
$(foreach f,$($1_FILES_CPP_SPEED),$(call compile-cpp-speed,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_C_SPEED),$(call compile-c-speed,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_ICPP_SPEED),$(call compile-icpp-speed,$1/$f,$($1_OBJDIR)/$(basename $f).io,$1))\
$(foreach f,$($1_FILES_CPP_SIZE),$(call compile-cpp-size,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_C_SIZE),$(call compile-c-size,$1/$f,$($1_OBJDIR)/$(basename $f).o,$1))\
$(foreach f,$($1_FILES_ICPP_SIZE),$(call compile-icpp-size,$1/$f,$($1_OBJDIR)/$(basename $f).io,$1))\
))
endef
# parse .upp file and process obtained variables
ifeq ($(single-package),y)
# prepare some variables neccesary early in the build process
define prep
$(eval $(shell cat $(call find-upp,$1) |sed -n $(PARSER)))
$(eval $1_FLAGS:=$(sort $($1_FLAGS) $(if $($1_SPEED),SPEED,$(if $($1_SIZE),SIZE,))))
$1_USES:=$(sort $($1_USES))
$1_FILES:=$(sort $($1_FILES))
$1_MACRO:=$(foreach f,$($1_FLAGS), -Dflag$f)
$1_OBJDIR:=$(call objdir,$1)
$1_SRC:=$(sort $(call get-src,$1))
$1_SRC_SPEED:=$(sort $(call get-src-speed,$1))
$1_SRC_SIZE:=$(sort $(call get-src-size,$1))
endef
# compile all .c/cpp files in package $1 and create .a
define build
$(eval $1_OBJFILES:=$(foreach f,$($1_SRC),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp),.i,).o)\
$(foreach f,$($1_SRC_SPEED),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_speed),.i,).o)\
$(foreach f,$($1_SRC_SIZE),$($1_OBJDIR)/$(basename $f)$(if $(filter $(suffix $f),.icpp_size),.i,).o))
$(eval $1_icpp-files+=$(foreach f,$($1_OBJFILES),$(if $(findstring .i.o,$f),$f,)))
$(eval $1_OBJFILES:=$(foreach f,$($1_FILES_CPP) $($1_FILES_CPP_SPEED) $($1_FILES_CPP_SIZE) $($1_FILES_C) $($1_FILES_C_SPEED) $($1_FILES_C_SIZE),$($1_OBJDIR)/$(basename $f).o))
$(eval $1_IOBJFILES:=$(foreach f,$($1_FILES_ICPP) $($1_FILES_ICPP_SPEED) $($1_FILES_ICPP_SIZE),$($1_OBJDIR)/$(basename $f).io))
$($1_OBJDIR)/$(notdir $1.a): \
$(foreach u,$($1_USES), $($u_OBJDIR)/$(notdir $u.a)) $($1_OBJFILES)
$($1_OBJDIR)/$(notdir $1.a): $($1_IOBJFILES) $($1_OBJFILES)
$(call echo," Creating archive $$@ ...")
$M$(AR) $$@ $(filter-out %.i.o,$($1_OBJFILES))
$M$(AR) $$@ $($1_OBJFILES)
$(eval $(call compile-all,$1))
endef
# create target to link libraries into executable
define link
$(eval ARCHIVES:=$(strip $(foreach u,$(ALLDEPS),$($u_icpp-files)) $(foreach u,$(ALLDEPS),$($u_OBJDIR)/$(notdir $u.a))))
$(eval ARCHIVES:=$(strip $(foreach u,$(ALLDEPS),$($u_IOBJFILES)) $(foreach u,$(ALLDEPS),$($u_OBJDIR)/$(notdir $u.a))))
$(eval ALLLIBS:=$(sort $(foreach u,$(ALLDEPS),$(foreach l,$($u_LIBS), -l$l))))
$(eval file-num=$(words $(foreach d,$(ALLDEPS), $($d_OBJFILES))))
$(eval file-num=$(words $(foreach d,$(ALLDEPS), $($d_OBJFILES) $(IOBJFILES))))
prep-dirs:
$(call echo," Preparing output directory structure ...")
$Mmkdir -p $(sort $(needed-dirs))
$(BINDIR)$1$(BINEXT): $(ARCHIVES)
$(BINPREFIX)$1$(BINEXT): $(ARCHIVES)
$(call echo," Linking $$@ ...")
$M$(CXX) -o $$@ $(if $(call has-flag,ALLSHARED,$1),-shared,$(if $(call has-flag,STATIC,$1),-static,)) $(LIBPATH) $(LDFLAGS) -Wl,--start-group $(ARCHIVES) $(ALLLIBS) -Wl,--end-group
$M$(CXX) -o $$@ $(if $(call has-flag,ALLSHARED,$($1_FLAGS)),-shared,$(if $(call has-flag,STATIC,$1),-static,)) $(LIBPATH) $(LDFLAGS) -Wl,--start-group $(ARCHIVES) $(ALLLIBS) -Wl,--end-group
endef
# Recursively parse all necessary packages
parse-deps=\
$(if $(filter $1,$(PKG)),$(if $(call confirm,$(USEMAINCFG)),\
$(info CMD:cat $(call find-upp,$(PKG)) |sed -n $(PARSER) OUT:$(shell cat $(call find-upp,$(PKG)) |sed -n $(PARSER)))$(eval $(shell cat $(call find-upp,$(PKG)) |sed -n $(PARSER)))\
$(eval CFG_FLAGS:=$(subst _, ,$(call lastword,$(subst :, ,$(firstword $($(PKG)_CFG))))))\
$(if $($(PKG)_CFG),,$(warning Package $(PKG) does not appear to be a main package!)$(warning Trying to continue anyway...)),),)\
$(foreach p,$1,\
$(eval $p_FLAGS:=$(sort $($p_FLAGS) $(FLAGS) $(PLATFORM) $(CFG_FLAGS)))\
$(if $(filter $p,$(ALLDEPS)),,\
$(if $S,,$(info Parsing package $p ...))\
$(eval ALLDEPS+= $p)\
$(eval $(call prep,$p))\
$(call parse-deps,$($p_USES))\
)\
$(eval ALLDEPS:=$(sort $(ALLDEPS)))\
)
endif # single-package
# call make to parse, compile and build package $1
define make-pkg
.PHONY: $1
$1:
$(if $(call confirm,$(SIMULATE)),@echo -n "SIMULATE: ";,)
$(if $(call confirm,$(SIMULATE)),,$M)+-\
$(MAKE) -f $(filename) $(if $(JOBS),-j$(JOBS) ,)--no-p PKG=$1 $(foreach v,$(cmdline-vars),$(call pass-var,$v))
$(MAKE) -f $(filename) $(if $(JOBS),-j$(JOBS) ,)--no-p PKG=$1 single-package=y $(foreach v,$(cmdline-vars),$(call pass-var,$v))
endef
.PHONY: default
default: all
# create targets for the main tasks
define main-targets
.PHONY: all start prep-dirs export
all: $(if $(single-package),start $(BINDIR)$(PKG)$(BINEXT),$(PKG))
all: $(if $(single-package),start $(BINPREFIX)$(PKG)$(BINEXT),$(PKG))
$(call echo,"$(if $(single-package), Package $(PKG),Everything) done.")
start: prep-dirs
$(call echo," Building package $(PKG) ($(file-num) files in $(words $(ALLDEPS)) packages, flags: $($(PKG)_FLAGS))")
$(call echo," Building package $(PKG)$(if $($(PKG)_DESC), [$($(PKG)_DESC)],) ($(file-num) files in $(words $(ALLDEPS)) packages, flags: $($(PKG)_FLAGS))")
endef
endif # if skip-parse
# create target to export makefile for package $1
define export-pkg
export_$1:
$(if $(call confirm,$(SIMULATE)),@echo -n "SIMULATE: ";,)
$(if $(call confirm,$(SIMULATE)),,$M)+-\
$(MAKE) -f $(filename) --no-p -r "EXPORT=$(EXPORT).$(subst /,_,$1)" PKG=$1 $(foreach v,$(cmdline-vars),$(call pass-var,$v)) export
$(MAKE) -f $(filename) --no-p -r single-package=y "EXPORT=$(EXPORT).$(subst /,_,$1)" PKG=$1 $(foreach v,$(cmdline-vars),$(call pass-var,$v)) export
endef
# create targets for help, clean up, export and developper targets
define additional-targets
.PHONY: help clean
help:
@head -n 104 $(filename)
@sed '/HELP.END/{s/.*//;q;}' $(filename)
clean:
$(call echo,Deleting $(OBJDIR))
$M rm -rf $(OBJDIR) || true
ifeq ($(single-package),y)
#For developers:
# print the parser to stdout (usage: make export-parser)
print-parser:
@sed -n 's/^#://p;' $(filename)
# print the parser to PARSER (usage: make export-parser PARSER=parser.sh)
export-parser:
@sed -n 's/^#://p;' $(filename) > $(PARSER)
# load $(PARSER) into this Makefile (usage: make update-parser PARSER=parser.new > Makefile.new)
update-parser:
@sed -n '/^#:/!s/.*/&/p;' $(filename)
@sed 's/.*/#:&/;' $(PARSER)
endef
#create export target
ifeq ($(words $(strip $(PKG))),1)
define export-targets
export:
$(call echo," Exporting makefile to $(EXPORT) ...")
$M$(MAKE) -f $(filename) -p -q -B -r "PKG=$(PKG)" $(foreach v,$(cmdline-vars),$(call export-var,$v)) \
|$(AWK) 'BEGIN{c=0;print ".PHONY:default";print "default: all";}{if(c==1&&substr($$$$0,0,1)!="#"){print $$$$0;}if(c==0&&$$$$0!="# Files"){next;}else{c=1;}}' \
|sed -n '1h;1!H;$$$${g;:d;s/[^\x0A\x0D]*:[\x0A\x0D][\x0A\x0D]*\([^\t]\)/\1/;t d;p;}'\
> '$(EXPORT)'
$M$(MAKE) -f $(filename) -p -q -B -r "PKG=$(PKG)" exporting=y $(foreach v,$(cmdline-vars),$(call export-var,$v)) \
|$(AWK) 'BEGIN{c=0;print ".PHONY:default";print "default: all";}{if(c==1&&substr($$$$0,0,1)!="#"){print $$$$0;}if(c==0&&$$$$0!="# Files"){next;}else{c=1;}}' \
|sed -n '1h;1!H;$$$${g;:d;s/[^\x0A\x0D]*:[\x0A\x0D][\x0A\x0D]*\([^\t]\)/\1/;t d;p;}'\
> '$(EXPORT)'
endef
else
define export-targets
export: $(foreach p,$(PKG),$(eval $(call export-pkg,$p)) export_$p)
$(call echo,"Exporting control makefile to $(EXPORT)")
$Mecho all:>'$(EXPORT)'
$Mecho " $M+-$(foreach p,$(PKG),make --no-p -f $(EXPORT).$(subst /,_,$p); )">>'$(EXPORT)'
endef
endif
endef
# clean-out the default rules SUFFIXES, we manage it ourselves
# clean-out the default rules SUFFIXES, we manage everything based on upp files
.SUFFIXES:
# call parse-upp and link on main package and create all directories
ifneq ($(skip-parse),y)
ifeq ($(single-package),y)
# Parse the upp files
$(if $S,,$(info Using $(JOBS) parallel jobs ...))
$(if $S,,$(info Parsing package files needed for $(PKG) ...))
ifeq ($(PARSER),)
$(eval $(shell sed -n 's/^#://p;' $(filename) | sh -s '$(PKG)' '$(NESTS)' '$(flags)'))
else
$(eval $(shell sh $(PARSER) '$(PKG)' '$(NESTS)' '$(flags)'))
endif
$(eval $(PKG)_FLAGS+=MAIN)
$(eval ALLDEPS:=$(call get-deps,$(PKG)))
$(foreach p,$(ALLDEPS),$(eval $(call prep,$p)))
endif # single-package
# Call link on main package and create all directories
# and build targets needed for current task
# OR
# run makefile for each package from $(PKG)
$(if $(skip-parse),,\
$(if $(single-package),\
$(eval $(PKG)_FLAGS:=MAIN)\
$(call parse-deps,$(PKG))\
$(foreach p,$(ALLDEPS),$(eval $(call build,$p)))\
$(eval $(call link,$(PKG)))\
$(foreach p,$(PKG),$(eval $(call make-pkg,$p)))))
$(if $(single-package),\
$(foreach p,$(ALLDEPS),$(eval $(call build,$p)))\
$(eval $(call link,$(PKG))),\
$(foreach p,$(PKG),$(eval $(call make-pkg,$p))))
endif # not skip-parse
$(eval $(main-targets))
ifneq ($(exporting),y)
$(eval $(additional-targets))
$(eval $(export-targets))
endif
# For each package, folowing variables are defined:
# $(PKG)_DESC
# $(PKG)_FILES
# $(PKG)_CFG
# $(PKG)_FLAGS
# $(PKG)_USES
# $(PKG)_LIBS
# $(PKG)_LINK
# $(PKG)_OPTS
# $(PKG)_TARGET
# $(PKG)_INC
# $(PKG)_ACCEPT
# $(PKG)_SPEED
# $(PKG)_SIZE
# Global variables defined when invoked for single package:
# ALLDEPS
# ARCHIVES
# ALLLIBS
# CFG_FLAGS
# Everything below this line is shell script used to parse .upp files
#:NESTS="$2"
#:FLAGS="$3"
#:
#:# GENERAL FUNCTIONS
#:FindUpp(){
#: pkg=`echo "$1" | sed -e 's|.*/||;'`
#: for n in $NESTS
#: do
#: if [ -f "$n/$1/$pkg.upp" ]
#: then
#: FILE="$n/$1/$pkg.upp"
#: NEST="$n"
#: return
#: fi
#: done
#:}
#:PrintFiles() {
#: local files="$1"
#: shift
#: local s
#: while [ "x" != "x$1" ]
#: do
#: s=$s"/[.]"$2"\$/ {s/[.]"$2"\$/."$3"/;s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval "$PKG"_"$1"+=&)|p;};"
#: shift 3
#: done
#: echo "$files" | sed -n -e "$s"
#:}
#:Pop(){
#: echo "$1"
#:}
#:Shift(){
#: shift
#: echo "$@"
#:}
#:
#:# SED PARSERS
#:# WARNING: Don't try to fix the ugly looking syntax of sed scripts - it is
#:# neccesary to make it work with BSD sed!
#:#
#:# Parser for booleans
#:p="/^description/! {"
#:# change (..(..)..) to (..[..]..)
#:p=$p"
#::a;
#:s/(\(.*\)(\([^()]*\))\(.*\))/(\1[\2]\3)/
#:t a;
#:"
#:# change (...) to ([...])
#:p=$p"s/(\(.*\))/([\1])/g;"
#:# delete spaces on left ...
#:p=$p":b;
#:s/\[\([^ ]*\)[ ][ ]*\([&|]\)\(.*\)\]/[\1\2\3]/;"
#:# ... and right side of operator
#:p=$p"s/\[\([^ ]*\)\([&|]\)[ ][ ]*\(.*\)\]/[\1\2\3]/;
#:t b;
#:"
#:# insert implicit &
#:p=$p":c;
#:s/\[\([a-zA-Z0-9_!&|]*\)[ ][ ]*\(.*\)\]/[\1\&\2]/;
#:t c;
#:"
#:# place {} around flags
#:p=$p"s/\([![&|]\)\([a-zA-Z0-9_][a-zA-Z0-9_]*\)/\1{\2}/g;"
#:# evalualte flags
#:p=$p"s/{\([^{}]*\)}/{\$(call has-flag,\1,$FLAGS)}/g;"
#:# evaluate negations
#:p=$p":d;
#:s/!{\([^{}]*\)}/{\$(if \1,,T)}/g;"
#:# evaluate ANDs
#:p=$p"s/{\([^{}]*\)}&{\([^{}]*\)}/{\$(call and,\1,\2)}/g;"
#:# evaluate ORs
#:p=$p"s/{\([^{}]*\)}|{\([^{}]*\)}/{\$(call or,\1,\2)}/g;"
#:# remove [] around already evaluated statements
#:p=$p"s/\[\({[^{}]*}\)\]/\1/g;
#:t d;
#:"
#:p=$p"s/({\([^{}]*\)})\(.*\)/\$(if \1,\2,)/g;
#:}"
#:
#:# File list parser
#:# strip quotes
#:f="s/\"//g;"
#:# remove separators
#:f=$f"s/,[ ]*[^,]*separator[ ]*,/,/g;"
#:# file.cpp optimize_xyz -> file.cpp_xyz
#:f=$f"s/,[ ]*\([^ ,]*\)[ ]*optimize_/,\1_/g;"
#:# drop modificators (charset, readonly, highlight, tabsize, font, ...)
#:f=$f"s/,[ ]*\([^ ,][^ ,]*\)[^,]*/,\1/g;"
#:# convert \ -> / and print
#:f=$f's/\\/\//g;p;'
#:
#:# Simple sections parser
#:# note: charset and blitz are ignored
#:Simples() {
#: echo "$2" | sed -n -e '
#: /^library[ ]*/ {s/^library[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"LIBS+=&)|;p;};"'
#: /^link[ ]*/ {s/^link[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"LINK+=&)|;p;};"'
#: /^acceptflags[ ]*/ {s/^acceptflags[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"ACCEPT+=&)|;p;};"'
#: /^mainconfig[ ]*/ {s/^mainconfig[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"CFG+=&)|;p;};"'
#: /^target[ ]*/ {s/^target[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"TARGET+=&)|;p;};"'
#: /^include[ ]*/ {s/^include[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"INC+=&)|;p;};"'
#: /^options[ ]*/ {s/^options[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"OPTS+=&)|;p;};"'
#: /^flags[ ]*/ {s/^flags[ ]*//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval $1"_"FLAGS+=&)|;p;};"
#:}
#:
#:# THE MAIN PARSER FUNCTION
#:Parse(){
#: PKG="$1"
#: FindUpp "$PKG"
#: echo '$(eval '$package'_NEST:='$NEST')'
#: echo '$(eval '$package'_FLAGS+='$FLAGS')'
#:
#: if [ -z "$FILE" ]
#: then
#: echo "ERROR: Package $PKG not found!"
#: exit 1
#: fi
#: # get the .upp contents, parse the boolean expresions and translate them to the language of make
#: UPP=`cat $FILE | tr "\n\r;" " \n" | sed -e "s/^[ ]*//g;s/[ ]*\$//g;s/[ ][ ]*/ /g; $p"`
#:# FILE SECTION PARSER
#: FILES=`echo "$UPP" | sed -n -e '/^'file'[ ]*/ {s/^'file'[ ]*//;s/.*/, & ,/;'"$f}" | tr "," "\n";`
#: PrintFiles "$FILES" \
#: FILES_CPP cpp cpp \
#: FILES_CPP_SPEED cpp_speed cpp \
#: FILES_CPP_SIZE cpp_size cpp \
#: FILES_ICPP icpp icpp \
#: FILES_ICPP_SPEED icpp_speed icpp \
#: FILES_ICPP_SIZE icpp_size icpp \
#: FILES_C c c \
#: FILES_C_SPEED c_speed c \
#: FILES_C_SIZE c_size c
#:# DESCRIPTION SECTION PARSER
#: echo "$UPP" | sed -n -e '/^'description'[ ]*/{s/^'description'[ ]*//;s/^"\(.*\)"$/\1/;s/\\[^ ]*$//;'"s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval "$PKG"_DESC=&)|p;}"
#:# USES SECTION PARSER
#: USES=$( echo "$UPP" | sed -n -e '/^'uses'[ ]*/{s/^'uses'[ ]*//;s/\\/\//g;/^\$/!s/[ ]*,[ ]*/ /g;p;}' )
#: echo "$USES" | sed -e "s|\"||g;s|^[ ]*||g;s|[ ]*\$||g;s|.*|\$(eval "$PKG"_USES+=&)|;"
#:# GLOBAL OPTIMIZATION FLAGS PARSER
#: echo "$UPP" | sed -n -e 's|^optimize_speed[ ]*|$(eval '"$PKG"'_SPEED=T)|p;
#: s|^optimize_size[ ]*|$(eval '"$PKG"'_SIZE=T)|p;'
#:# SIMPLE SECTIONS PARSERS
#: Simples "$PKG" "$UPP"
#:}
#:
#:# MAIN LOOP
#:NEEDED="$1"
#:PARSED=""
#:while [ -n "$NEEDED" ]
#:do
#: package=`Pop $NEEDED`
#: NEEDED=`Shift $NEEDED`
#: Parse $package;
#: PARSED="$PARSED $package"
#: uses=$( echo "$USES" | sed -e 's/\\/\//g;/^\$(/{s/.*, \([^, ]*\),[)]*$/\1/;}' )
#: for dep in $uses
#: do
#: if [ -z "`echo " $NEEDED $PARSED " | grep " $dep "`" ]
#: then
#: NEEDED="$NEEDED $dep"
#: fi
#: done
#:done;
#:#
#:echo '$(eval PARSED:='"$PARSED"')'