From ff613ca996c68ed9eee4d82e43913cc161a32e51 Mon Sep 17 00:00:00 2001 From: dolik Date: Fri, 20 Aug 2010 23:10:09 +0000 Subject: [PATCH] lpbuild: universal Makefile updated git-svn-id: svn://ultimatepp.org/upp/trunk@2630 f0d560ea-af0d-0410-9eb7-867de7ffcac7 --- uppbox/lpbuild/Makefile | 626 +++++++++++++++++++++------------------- 1 file changed, 333 insertions(+), 293 deletions(-) diff --git a/uppbox/lpbuild/Makefile b/uppbox/lpbuild/Makefile index a1bf92a45..cb3d0c138 100755 --- a/uppbox/lpbuild/Makefile +++ b/uppbox/lpbuild/Makefile @@ -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/>@[^@]*@@\n@@/@<({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"')'