+UppBuilder: upp file parser and makefile generator

git-svn-id: svn://ultimatepp.org/upp/trunk@4562 f0d560ea-af0d-0410-9eb7-867de7ffcac7
This commit is contained in:
dolik 2012-02-08 16:54:03 +00:00
parent c3c2ffc1be
commit 8f918d4f6d
18 changed files with 1753 additions and 0 deletions

251
uppbox/UppBuilder/Make.hpp Normal file
View file

@ -0,0 +1,251 @@
#include <time.h>
String ParseCustomStep(const String& s){
String cmd;
for(const char* c=~s;*c;c++){
if(c[0]=='\\' && c[1]=='\"') continue;
if(strncmp(c,"$(PATH)",7)==0){
cmd+="$$f";
c+=6;
} else if(strncmp(c,"$(DIR)",6)==0){
cmd+="`dirname $$f`";
c+=5;
} else if(strncmp(c,"$(FILE)",7)==0){
cmd+="`basename $$f`";
c+=6;
} else if(strncmp(c,"$(TITLE)",8)==0){
cmd+="`basename $$f | sed 's/[.][^.]*$//'`";
c+=7;
} else if(strncmp(c,"$(OUTDIR)",9)==0){
cmd+="$(BIN)/";
c+=8;
} else
cmd+=*c;
// $(INCLUDE),$(!) etc. not implemented yet
}
return cmd;
}
String CustomRule(String rule, const String& pkg){
String ext=ReadValue(rule);
String cmd=ParseCustomStep(ReadValue(rule));
//ignore last part for simplicity
if (cmd.IsEmpty() || ext.IsEmpty())
return "";
if (ext=="pre-link")
return " $E cd $("+pkg+"_src) &&"
"echo \" Applying custom build pre-link step "
"(warning: this is an experimental feature)\"; "
+cmd+";\n";
if (ext=="post-link")
return " $E cd $("+pkg+"_src) &&"
"echo \" Applying custom build post-link step "
"(warning: this is an experimental feature)\"; "
+cmd+";\n";
return " $E cd $("+pkg+"_src) && for f in *"+ext+"; "
"do echo \" Applying custom build step on $$f "
"(warning: this is an experimental feature)\"; "
+cmd+"; done;\n";
}
void Parser::Process(){
String uppout=GetEnv("OUT","/tmp/_out");
String makefile=uppout+"/Makefile";
FILE* mf=fopen(makefile,"w");
if(!mf){
printf("Failed to open file %s\n", ~makefile);
exit(1);
}
fprintf(mf, "# This is an automatically generated makefile for %s package\n"
"# You should never need to modify it...\n", ~main);
bool dep = GetEnv("DEPS","0")=="1";
bool color = GetEnv("COLOR","0")=="1";
String includes;
Vector<String> inc;
Split(GetEnv("INCPATHS"),inc);
for(int i=0; i<nests.GetCount(); i++)
includes+=" -I"+nests[i];
for(int i=0; i<inc.GetCount(); i++)
includes+=" -I"+inc[i];
String libpaths;
Vector<String> lpaths;
Split(GetEnv("LIBPATHS"),lpaths);
for(int i=0; i<lpaths.GetCount(); i++)
libpaths+=" -L"+lpaths[i];
time_t rawtime;
time(&rawtime);
char date[90];
struct tm* t = localtime(&rawtime);
sprintf(date," -DbmYEAR=%i -DbmMONTH=%i -DbmDAY=%i -DbmHOUR=%i -DbmMINUTE=%i -DbmSECOND=%i",
t->tm_year+1900, t->tm_mon, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec);
fprintf(mf, "OUT:=%s\n"
"BIN:=%s\n"
"CFLAGS:=%s\n"
"CXXFLAGS:=%s\n"
"SPEEDFLAGS:=%s\n"
"SIZEFLAGS:=%s\n"
"LDFLAGS:=%s\n"
"CC:=%s\n"
"CXX:=%s\n"
"AR:=%s\n"
"MKDIRP:=%s\n"
"E:=%s\n"
"M:=%s\n"
"C:=%s\n"
"B:=%s\n"
"N:=%s\n"
"mc=$M \" Compiling $C$<$N\"\n"
"ma=$M \"=> $BCreating archive$N $C$@$N\"\n"
"cc=$E $(CC) -c -x c %s $< -o $@ $(CPPFLAGS) %s\n"
"cxx=$E $(CXX) -c -x c++ %s $< -o $@ $(CPPFLAGS) %s\n"
"includes:=%s\n"
"LIBPATHS:=%s\n"
"default: build\n"
".PHONY:build clean createdirs custom default\n\n",
~uppout, ~GetEnv("BIN","$(OUT)/bin"),
~GetEnv("CFLAGS","-O2"),
~GetEnv("CXXFLAGS","$(CFLAGS)"),
~GetEnv("SPEEDFLAGS","-O3 -ffunction-sections -fdata-sections"),
~GetEnv("SIZEFLAGS","-Os -finline-limit=20 -ffunction-sections -fdata-sections"),
~GetEnv("LDFLAGS","-Wl,--gc-sections -Wl,-s -Wl,-O,2"),
~GetEnv("CC","$(CC)"), ~GetEnv("CXX","$(CXX)"),
~GetEnv("AR","ar -src"), ~GetEnv("MKDIRP","mkdir -p"),
GetEnv("ECHO","0")=="1"?"":"@",
GetEnv("SILENT","0")=="1"?"@true":(color?"@echo -e":"@echo"),
color?(const char*)("\\e[3"+GetEnv("HIGHLIGHT","4")+"m"):"",
color?"\\e[1m":"", color?"\\e[0m":"",
dep?"-MMD":"", date, dep?"-MMD":"",
date, ~includes, ~libpaths);
String icpps, archives, prelink, postlink, custom;
Vector<String> dirs;
dirs.Add("$(BIN)");
for(int i=0;i<pkgs.GetCount();i++){
const String& pkg=pkgs[i].name;
String base=BaseName(pkg);
String objlist;
Vector<String> pflags;
String opt;
//we add the internal includes twice, to fix the case with relative paths... isn't there a better solution?
opt=EvalVector(pkgs[i].include,i,"-I");
opt+=EvalVector(pkgs[i].include,i,"-I$("+pkg+"_src)/");
opt+=EvalVector(pkgs[i].option,i,"");
for(int j=0;j<pkgs[i].flags.GetCount();j++)
opt+=" -Dflag"+pkgs[i].flags[j];
String fdir=pkg+"/"+FlagDir(pkgs[i].flags);
fprintf(mf, "%s", ~(pkg+"_src="+pkgs[i].dir+"\n"+
pkg+"_out=$(OUT)/"+fdir+"\n"+
pkg+"_opt=$(includes) "+opt+"\n"));
for(int j=0; j<pkgs[i].files.GetCount(); j++){
const File& f=pkgs[i].files[j];
dirs.AddUnique(DirName("$("+pkg+"_out)/"+f.name));
if(f.lang == "brc"){
fprintf(mf, "$(%s_out)/%s.o: $(%s_src)/%s\n"
" $(mc)\n"
" $E sh $(OUT)/brc.sh $< > $(%s_out)/%s.cpp\n"
" $E $(CXX) -c -x c++ $(%s_out)/%s.cpp -o $@ $(CPPFLAGS) $(CXXFLAGS)\n",
~pkg, ~f.name, ~pkg, ~f.name, ~pkg, ~f.name, ~pkg, ~f.name);
objlist+=" $("+pkg+"_out)/"+f.name+".o";
continue;
}
String ext,fopt;
if(f.lang=="c++")
opt="$(CXXFLAGS) ";
else if(f.lang=="c")
opt="$(CFLAGS) ";
if(pkgs[i].opt==SPEED) ext=".speed.o";
else if(pkgs[i].opt==SIZE) ext=".size.o";
else{
if(f.opt==SPEED) ext=".speed.o";
if(f.opt==SIZE) ext=".size.o";
else ext=".o";
}
if(ext==".speed.o")
fopt+="$(SPEEDFLAGS)";
else if(ext==".size.o")
fopt+="$(SIZEFLAGS)";
String fn="$("+pkg+"_out)/" + f.name + ext;
if(dep){
String dep = uppout + "/" + fdir + "/" + f.name + ext;
dep[dep.GetCount()-1]='d';
dep=LoadFile(dep);
if(dep.GetCount())
fprintf(mf, "%s", ~dep);
else
fprintf(mf, "%s: $("+pkg+"_src)/%s\n", ~fn, ~f.name);
} else {
fprintf(mf, "%s: $("+pkg+"_src)/%s\n", ~fn, ~f.name);
}
fprintf(mf," $(mc)\n"
" $(%s) %s $(%s_opt)\n",
~f.lang, ~fopt, ~pkg);
if(f.name.Mid(f.name.GetCount()-4)=="icpp")
icpps+=" "+fn;
else
objlist+=" "+fn;
}
fprintf(mf,"%s_custom:\n", ~pkgs[i].name);
for(int j=0;j<pkgs[i].custom.GetCount();j++){
if(EvalOpt(pkgs[i].custom[j],i)){
if(pkgs[i].custom[j].item.Compare("\"pre-link\"",10)==0)
prelink+=CustomRule(pkgs[i].custom[j].item, pkgs[i].name);
else if(pkgs[i].custom[j].item.Compare("\"post-link\"",11)==0)
postlink+=CustomRule(pkgs[i].custom[j].item, pkgs[i].name);
else{
String rule = CustomRule(pkgs[i].custom[j].item, pkgs[i].name);
if(rule=="") continue;
fprintf(mf,"%s",~rule);
custom+=" "+pkgs[i].name+"_custom";
}
}
}
fprintf(mf,"$(%s_out)/%s.a:%s\n"
" $(ma)\n"
" $E $(AR) $@ $^\n\n",
~pkg, ~base, ~objlist);
archives="$("+pkg+"_out)/"+base+".a "+archives;
}
String liblist,optlist,dirlist;
for(int i=0;i<libs.GetCount();i++)
liblist+=" -l"+libs[i];
for(int i=0;i<linkopts.GetCount();i++)
optlist+=" "+linkopts[i];
for(int i=0;i<dirs.GetCount();i++)
dirlist+=" "+dirs[i];
String target = GetEnv("TARGET");
if(target.IsEmpty()){
target=BaseName(main);
for(int i=0;i<pkgs[0].target.GetCount();i++){
if(EvalOpt(pkgs[0].target[i],0)){
target = pkgs[0].target[i].item;
break;
}
}
}
if(target[0]!='/')
target="$(BIN)/"+target;
dirs.AddUnique(DirName(target));
fprintf(mf, "build: createdirs %s %s\n", ~custom, ~target);
fprintf(mf, "%s: %s %s\n"
" $M \"$BLinking$N $C$@$N\"\n"
" $E $(CXX) -o $@ $(LDFLAGS) $(LIBPATHS) %s -Wl,--start-group $^ %s -Wl,--end-group\n"
"%s",
/*~prelink,*/ ~target, ~icpps, ~archives, ~optlist, ~liblist, ~postlink);
fprintf(mf, "createdirs:\n"
" $M \" $BCreating necessary directories ...$N\"\n"
" $E mkdir -p%s\n",
~dirlist);
String clean;
for(int i=0;i<pkgs.GetCount();i++)
clean+=" $("+pkgs[i].name+"_out)";
fprintf(mf,"clean:\n"
" $E rm -rf %s",~clean);
fclose(mf);
}

View file

@ -0,0 +1,213 @@
########################################################################
# __ __ _ _ __ __ __ _ _ ___ ___ __ __ ___ #
# ( )( ) _( )_ _( )_ ( \/ ) ( ) ( )/ )( _)( _)( )( ) ( _) #
# )(__)( (_ _)(_ _) ) ( /__\ ) \ ) _) ) _) )( )(__ ) _) #
# \____/ (_) (_) (_/\/\_)(_)(_)(_)\_)(___)(_) (__)(____)(___) #
# #
############################## USAGE ###################################
# This makefile is controlled through a set of variables, similar to #
# any other makefile. The prefered way to change them is via command #
# line, e.g.: make PKG="ide usvn" CXX=g++ #
# #
# All of the variables that can control the bahaviour are listed below #
# in DEFAULTS section. Also their default value is shown here. Notice #
# that many of them can contain shell commands (enclosed in ``) which #
# are evaluated before parsing and building each package.
# #
# For boolean type of variables value "1" represents true, anything #
# else is evaluated as false. #
############################# TARGETS ##################################
# This makefile defines five targets that are meant to be used #
# directly by user from the command line: #
# #
# all - builds all (default if any packages are specified) #
# help - shows this help (default if no packages are given) #
# simulate - prints all commands, without executing them #
# clean - deletes all compiled files related to given packages #
# dist-clean - deletes the entire OUT directory (including parsers) #
############################# EXAMPLES #################################
# Typical usage: #
# make PKG=ide FLAGS="GCC" #
# More complicated usage: #
# make PKG="ide UWord Bombs" FLAGS="GCC .NOGTK" ECHO=1 #
# Show help: #
# make help #
# Silent mode: #
# make PKG=ide SILENT=1 #
############################# DEFAULTS #################################
# List of packages to build (space separated). If empty, this help #
# text will be shown. #
PKG:=
# Paths where to look for packages. #
NESTS:=uppsrc bazaar examples reference tutorial $$HOME/MyApps
# Building flags (as in TheIDE).
FLAGS:=GCC
# Additional include paths for compiler (without the leading "-I"). #
INCPATHS:= /usr/include/freetype2 /usr/include/gtk-2.0
INCPATHS+= /usr/local/include/gtk-2.0 /usr/include/glib-2.0
INCPATHS+= /usr/local/include/glib-2.0 /usr/lib/glib-2.0/include
INCPATHS+= /usr/local/lib/glib-2.0/include /usr/lib/gtk-2.0/include
INCPATHS+= /usr/local/lib/gtk-2.0/include /usr/include/cairo
INCPATHS+= /usr/include/pango-1.0 /usr/include/atk-1.0
INCPATHS+= /usr/X11R6/include /usr/X11R6/include/freetype2
INCPATHS+= /usr/X11R6/include/gtk-2.0 /usr/X11R6/include/glib-2.0
INCPATHS+= /usr/X11R6/lib/glib-2.0/include
INCPATHS+= /usr/X11R6/lib/gtk-2.0/include /usr/X11R6/include/cairo
INCPATHS+= /usr/X11R6/include/pango-1.0 /usr/X11R6/include/atk-1.0
INCPATHS+= /usr/local/include/cairo /usr/local/include/pango-1.0
INCPATHS+= /usr/local/include/atk-1.0 /usr/local/include
INCPATHS+= /usr/local/include/libpng /usr/include/gdk-pixbuf-2.0
INCPATHS+= /usr/lib/i386-linux-gnu/glib-2.0/include
INCPATHS+= /usr/lib/x86_64-linux-gnu/glib-2.0/include
# Paths to libraries for linker. #
LIBPATHS:=/usr/X11R6/lib /usr/lib /usr/local/lib
# Directory to store intermediate object files #
OUT:=_out
# Directory where the resulting binaries will be stored #
BIN:=$(OUT)/bin
# ar command #
AR:=ar -src
# Command to create directories equivalent to "mkdir -p". The only #
# good reason to change is if your platform uses mkdir command which #
# doesn't support the -p option #
MKDIRP:=mkdir -p
# C compiler command #
CC:=cc
# C++ compiler command #
CXX:=c++
# Options for C/C++ preprocessor #
CPPFLAGS:=
# Options for C compiler #
CFLAGS:=
# Options for C++ compiler #
CXXFLAGS:=
# Options for linker #
LDFLAGS:=-Wl,--gc-sections -Wl,-s -Wl,-O,2
# Additional options for speed optimization #
SPEEDFLAGS:=-O3 -ffunction-sections -fdata-sections
# Additional options for size optimization #
SIZEFLAGS:=-Os -finline-limit=20 -ffunction-sections -fdata-sections
# Optimization, supply SPEED or SIZE to optimize the executables #
OPT:=
# Platform flag, added to FLAGS, can include shell commands #
PLATFORM:=`uname | tr a-z A-Z` POSIX
# Suppress all messagges #
SILENT:=0
# Print each executed command #
ECHO:=0
# Use dependency files. This will probably work only with gcc-like #
# compilers (requires -MD option). The build usually works correctly #
# even without specifying the dependencies unless you modify included #
# files (*.h, *.lay, ...) without modifying the *.c/cpp files that #
# include them. #
DEPS:=0
# Run the executable(s) sequntially when compiled #
RUN:=0
# Override default name of final executable (absolute or relative path)
TARGET:=
# Add flags from based on mainconfig section (1-based index, set #
# to 0 to disable completely #
USEMAINCFG:=1
# Ask for some additional details during parsing #
INTERACTIVE:=0
# Use colorized output, recognized values are 0, 1 or auto #
COLOR:=auto
# What color should be used in colorized mode, number from 0 to 7 #
HIGHLIGHT:=4
TIME:=TIMEFORMAT=" time taken: %Es"; time
########################################################################
m:=[ "$(SILENT)" = "1" ] || echo
e:=@ #comment out for debugging
dbgparser:=-DflagDEBUG -ggdb3
thisfile:=$(MAKEFILE_LIST)$(.MAKEFILE_LIST)
color:=if [ "$(COLOR)" = "auto" ]; then [ -t 1 ] && c=1 || c=0; else c="$(COLOR)"; fi
pass:=$(color); UPP_NESTS="$(NESTS)" UPP_FLAGS="$(FLAGS)" \
UPP_INCPATHS="$(INCPATHS)" UPP_LIBPATHS="$(LIBPATHS)" UPP_OUT="$(OUT)" \
UPP_BIN="$(BIN)" UPP_AR="$(AR)" UPP_MKDIRP="$(MKDIRP)" UPP_CC="$(CC)" \
UPP_CXX="$(CXX)" UPP_CFLAGS="$(CFLAGS)" UPP_CXXFLAGS="$(CXXFLAGS)" \
UPP_LDFLAGS="$(LDFLAGS)" UPP_SPEEDFLAGS="$(SPEEDFLAGS)" \
UPP_SIZEFLAGS="$(SIZEFLAGS)" UPP_PLATFORM="$(PLATFORM)" \
UPP_SILENT="$(SILENT)" UPP_ECHO="$(ECHO)" UPP_DEPS="$(DEPS)" \
UPP_RUN="$(RUN)" UPP_TARGET="$(TARGET)" UPP_USEMAINCFG="$(USEMAINCFG)" \
UPP_OPT="$(OPT)" UPP_INTERACTIVE="$(INTERACTIVE)" UPP_COLOR="$$c" \
UPP_HIGHLIGHT="$(HIGHLIGHT)"
all: no-package-specified build
do-build: $(OUT)/parser $(OUT)/brc.sh $(OUT)/color.sh
+$e set -e; $(color); B=""; N=""; C=""; color=""; \
[ $$c -eq 1 ] && color="$(SHELL) $(OUT)/color.sh" && B="\e[1m" && N="\e[0m" && C="\e[1;3$(HIGHLIGHT)m"; \
for pkg in $(PKG) ; do \
$m $${B}Parsing package $$C$$pkg$$N $$B...$$N; \
$(TIME) $(pass) $$color $(OUT)/parser $$pkg; \
$m $${B}Building package$$N $$C$$pkg$$N $$B...$$N; \
$(TIME) $$color $(MAKE) -j 3 -f $(OUT)/Makefile && \
$m $${B}Package$$N $$C$$pkg$$N$$B finished ...$$N; \
done; set +e
no-package-specified:
$e if [ -z "$(PKG)" ]; then sed '/^$$/{s/.*//;q;}' $(thisfile) && false; fi
$(OUT)/parser: $(OUT)/parser.cpp
@$m Compiling parser...
+$e $(TIME) $(CXX) -x c++ -o $@ $^ $(dbgparser)
$(OUT)/parser.cpp: $(thisfile)
@$m "Extracting parser..."
+$e [ -d $(OUT) ] || $(MKDIRP) $(OUT)
+$e $(TIME) sed -n 's/^#://p;' $< | base64 -d | gzip -d > $@
$(OUT)/brc.sh: $(thisfile)
@$m "Extracting brc parser..."
+$e [ -d $(OUT) ] || $(MKDIRP) $(OUT)
+$e $(TIME) sed -n 's/^#~//p;' $< | base64 -d | gzip -d > $@
$(OUT)/color.sh: $(thisfile)
@$m "Extracting colorizer script..."
+$e [ -d $(OUT) ] || $(MKDIRP) $(OUT)
+$e $(TIME) sed -n 's/^#%//p;' $< | base64 -d | gzip -d > $@
build: do-build
$e for pkg in $(PKG); do \
if [ "$(RUN)" = "1" ]; then \
for target in "$(TARGET)" "$(BIN)/$(TARGET)" "$(BIN)/$$pkg"; do \
[ -f "$$target" ] && break; \
done; \
[ -e "$$target" ] && { $m "Running $$target"; } && $$target; \
fi; \
done
simulate: $(OUT)/Makefile
+$e for pkg in $(PKG) ; do \
$m Parsing package $$pkg ... && \
$(pass) $(OUT)/parser $$pkg && \
$m Building package $$pkg ... && \
$(MAKE) -nf $(OUT)/Makefile; \
done
help:
$e sed '/^$$/{s/.*//;q;}' $(thisfile);
clean:
+$e for pkg in $(PKG); do \
$m Parsing package $$pkg... && \
$(pass) $(OUT)/parser $$pkg && \
$m Cleaning package $$pkg && \
$(MAKE) -f $(OUT)/Makefile clean; \
done
dist-clean:
@$m Deleting $(OUT) ...
$e rm -rf $(OUT)
parser-clean:
@$m Deleting parser files ...
$e rm -f $(OUT)/parser $(OUT)/parser.cpp
.SUFFIXES:
.PHONY: all help simulate clean dist-clean parser-clean \
build do-build no-package-specified

View file

@ -0,0 +1,638 @@
#include <unistd.h>
#include <ctype.h>
#include <new>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
template<class T>
inline void NEVER(T s){
if(s){
fprintf(stderr,"ERROR: Something that should never happened has happened...\n");
exit(1);
}
};
template<class T>
class Vector{
int size,alloc;
T* p;
public:
Vector():size(0), alloc(0), p(0) {Resize(2);}
~Vector() {Resize(0);}
inline int GetCount()const {return size;}
inline T& operator[](int n) {return p[n];}
inline const T& operator[](int n) const {return p[n];}
T& Add(){
if(size>=alloc)
Resize(2*alloc);
return *(new(p+size++) T);
}
T& Add(const T& x) { return Add()=x; }
T& AddUnique(const T& v){
int pos=Find(v);
if(pos>=0) return p[pos];
return Add(v);
}
int Find(const T& v) const{
for(int i=0;i<size;i++){
if(v==p[i]) return i;
}
return -1;
}
void Resize(int len){
if(size>len){
for(int i=len;i<size;i++)
(p+i)->~T();
size=len;
}
p = (T*)realloc(p,len*sizeof(T));
alloc=len;
}
private:
Vector<T>& operator=(const Vector<T>& v){}
};
class String {
char* p;
public:
String() : p(strdup("")) {}
String(const String& s) : p(strdup(s.p)){}
String(const char* s) : p(strdup(s)) {}
~String() {free(p);}
char& operator[](const int n) const {return p[n];}
bool IsEmpty() const {return *p == 0;}
const char* operator~() const {return p;}
operator const char*() const {return p;}
String& operator=(const String& s) {return operator=(s.p);}
bool operator==(const char* s) const {return !strcmp(p, s);}
bool operator==(const String& s) const {return !strcmp(p, s.p);}
void Clear() {free(p); p = strdup("");}
int GetCount() const {return strlen(p);}
String& operator+=(const String& s){
const int lenp = strlen(p);
const int lens = strlen(s.p) + 1;
p = static_cast<char*>(realloc(p, lenp + lens));
NEVER(!p);
memmove(p+lenp, s.p, lens);
return *this;
}
String& operator+=(char s){
const int lenp = strlen(p);
p = static_cast<char*>(realloc(p, lenp + 2));
NEVER(!p);
p[lenp] = s;
p[lenp+1] = 0;
return *this;
}
String& operator=(const char* s){
if (p != s) {
char *copy = strdup(s);
free(p);
p = copy;
}
return *this;
}
int Compare(const char* s, int len=-1) const {
return len?strncmp(p,s,len):strcmp(p,s);
}
int FindLast(const char c) const {
for(int i=strlen(p)-1;i>=0;i--)
if(p[i]==c) return i;
return -1;
}
int Find(const char* s) const {
int lens=strlen(s);
int lenp=strlen(p);
for(int i=0;i<lenp-lens;i++)
if(strncmp(p+i,s,lens)) return i;
return -1;
}
String Mid(const int start, const int length=-1) const{
int len = strlen(p)-start;
if(len<0){
fprintf(stderr, "Error: Invalid parameters to Mid!");
exit(1);
};
if (start+len>length && length>0)
len = length;
String s;
free(s.p);
s.p = static_cast<char*>(malloc(len+1));
NEVER(!s.p);
memcpy(s.p, p + start, len);
s.p[len] = '\0';
return s;
}
friend String operator+(const String& lhs, const String& rhs);
friend int operator<(const String& lhs, const String& rhs);
};
inline String operator+(const String& lhs, const String& rhs){
return String(lhs) += rhs;
}
inline int operator<(const String& lhs, const String& rhs){
return lhs.Compare(~rhs);
}
String ReadValue(String& s,char delim1 = ',',char delim2 = ','){
if(s=="") return "";
String r;
int skip=0;
while((s[skip]==delim1 || s[skip] == delim2 || s[skip]<=32) && s[skip]!=0)
skip++;
s = s.Mid(skip);
bool q=false;
if(s[0]=='\"'){
s = s.Mid(1);
q = true;
}
const char* c = s;
for(; *c; c++){
if(*c==delim1 || *c==delim2 || (q && *c=='\"') && c[-1] != '\\') break;
if(*c<=(q?0:32)) continue;
r += *c;
}
if(q) c++;
s = c;
return r;
};
bool Consume(String& s,const char* section){
int len=strlen(section);
if(s.Compare(section,len)==0){
s = s.Mid(len);
return true;
}
return false;
};
enum Opt{NONE, SPEED, SIZE};
enum OptState { UNKNOWN, NO, YES };
struct File{
Opt opt;
String name, lang;
File():opt(NONE){};
};
bool IsSource(File& f){
int pos=f.name.FindLast('.');
if(!pos)
return false;
String ext = f.name.Mid(pos+1);
for(int i=0;i<ext.GetCount();i++)
ext[i]=tolower(ext[i]);
if (ext.Compare("cpp") == 0
|| ext.Compare("icpp") == 0
|| ext.Compare("cc") == 0
|| ext.Compare("cxx") == 0){
f.lang="cxx";
return true;
} else if (ext.Compare("c") == 0){
f.lang="cc";
return true;
} else if (ext.Compare("brc") == 0){
f.lang="brc";
return true;
}
return false;
};
void Slashes(String& s){
for(int i=0; i<s.GetCount(); i++){
if(s[i]=='\\') s[i]='/';
}
};
struct OptItem{
String when;
String item;
mutable OptState state;
OptItem():state(UNKNOWN){};
};
bool LoadOpts(String& s, const char *key, Vector<OptItem>& v) {
if(Consume(s,key)) {
if(s[0]==' ')
s = s.Mid(1);
String when;
if(s[0]=='('){
when=s;
int len = 0;
int lvl = 0;
for(const char* c=when; *c; c++) {
len++;
if(*c == '(') lvl++;
else if(*c == ')'){
lvl--;
if(lvl == 0) break;
}
}
when = s.Mid(0,len);
s = s.Mid(len);
}
do {
OptItem& m=v.Add();
m.when = when;
m.item = ReadValue(s,',');
Slashes(m.item);
}while(s[0] == ',');
return true;
}
return false;
};
String BaseName(const String& s){
int pos=s.FindLast('/');
if(!pos)
return s;
return s.Mid(pos+1);
};
String DirName(const String& s){
int pos=s.FindLast('/');
if(!pos)
return "";
return s.Mid(0,pos);
};
inline String LoadFile(const String& fn){
String r;
char b[1000];
FILE* f=fopen(~fn,"r");
if(!f) return "";
int n=0;
while(!feof(f)){
n+=fread(b,1,1000,f);
r+=b;
//this is a hack, correct implementation would not need it
r=r.Mid(0,n);
}
return r;
}
inline String GetEnv(const char* n,const char* s=""){
String p("UPP_");
p+=n;
const char* r=getenv(p);
return r?r:s;
};
struct Upp{
String name,description,dir;
Opt opt;
Vector<OptItem> option, link, library, addflags, target, uses, include, custom;
Vector<String> accepts, cfgk, cfgv, flags;
Vector<File> files;
Upp() : opt(NONE){};
Upp(const String& package) : name(package), opt(NONE){};
Upp(const String& package,const Vector<String>& nests)
: opt(NONE) {Load(package,nests);}
bool operator==(const Upp& u)const {return name==u.name;}
Upp& operator=(const Upp& u){NEVER(true); return *this;}
String GetClause(FILE* f){
String r;
int l=0;
bool q=false;
while(1){
int c = fgetc(f);
if(c==EOF || (!q && c==';')) break;
if(c<=32){
if(!r.IsEmpty() && r[r.GetCount()-1]!=' ')
r += ' ';
continue;
} else
if (c == '\"' && l != '\\') {
q=!q;
}
l = c;
r += c;
}
return r;
};
Upp& Load(const String& package,const Vector<String>& nests){
name=package;
char temp[FILENAME_MAX];
String cwd = getcwd(temp, FILENAME_MAX);
cwd += "/";
String fn;
FILE* f(NULL);
for(int i=0; i<nests.GetCount(); i++){
dir = (nests[i][0]=='/'?"":cwd) + nests[i] + "/" + package;
fn = dir + "/" + BaseName(package) + ".upp";
f = fopen(fn,"r");
if(f) break;
}
if(!f){
fprintf(stderr, "Error: Package %s not found!\n", ~package);
exit(1);
}
String s;
while(1){
s = GetClause(f);
if((LoadOpts(s, "options", option) ||
LoadOpts(s, "link", link) ||
LoadOpts(s, "library", library) ||
LoadOpts(s, "flags", addflags) ||
LoadOpts(s, "target", target) ||
LoadOpts(s, "uses", uses) ||
LoadOpts(s, "include", include))){
} else
if(Consume(s,"description")){
int pos=s.Find("\\377");
if(pos)
s = s.Mid(0,pos) + "\"";
} else
if(Consume(s,"acceptflags")) {
do{
accepts.Add(ReadValue(s));
}while(s[0] == ',');
} else
if(Consume(s,"noblitz")){
// ignored for now
} else
if(Consume(s,"mainconfig")) {
do {
cfgk.Add(ReadValue(s));
while(Consume(s," ") || Consume(s,"\""));
Consume(s,"=");
cfgv.Add(ReadValue(s));
while(Consume(s," ") || Consume(s,"\""));
}while(s[0] == ',');
} else
if(Consume(s,"optimize_speed"))
opt = SPEED;
else
if(Consume(s,"optimize_size"))
opt = SIZE;
else
if(Consume(s,"file")){
do{
File f;
f.name = ReadValue(s,' ');
bool separator=false;
while(*s && s[0]!=','){
String str = ReadValue(s,' ');
if(Consume(str,"optimize_speed"))
f.opt = SPEED;
else if(Consume(str,"optimize_size"))
f.opt = SIZE;
else if(Consume(str,"separator"))
separator=true;
}
if(!separator && IsSource(f)){
Slashes(f.name);
files.Add(f);
}
}while(s[0] == ',');
} else
if(Consume(s,"custom")){
if(s[0]==' ')
s = s.Mid(1);
OptItem& m=custom.Add();
int len = 0;
if(s[0]=='('){
int lvl = 0;
for(const char* c=s; *c; c++) {
len++;
if(*c == '(') lvl++;
else if(*c == ')'){
lvl--;
if(lvl == 0) break;
}
}
m.when = s.Mid(0,len);
}
m.item = s.Mid(len);
} else
if(feof(f))
break;
}
fclose(f);
return *this;
};
};
inline void Split(String s,Vector<String>& v){
while(s.GetCount()) v.AddUnique(ReadValue(s,' '));
}
String FlagDir(const Vector<String>& v){
Vector<String> r;
//simple insertion sort
r.Resize(v.GetCount());
for(int i=0;i<v.GetCount();i++)
r.Add(v[i]);
for (int j=1; j<v.GetCount(); ++j) {
const char* c = ~v[j];
int i=j-1;
while(i>=0 && r[i].Compare(c)>0) {
r[i+1]=r[i];
i--;
}
r[i+1]=c;
}
String s=r[0];
for(int i=1;i<r.GetCount();i++)
s+="."+r[i];
return s;
}
class Parser{
Vector<Upp> pkgs;
String main;
public:
Vector<String> nests, flags, dflags, libs, linkopts;
static inline bool IsId(int c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || (c >= '0' && c <= '9');
}
bool sMatchFlag(String& s,int pkg){
if(Consume(s,"!"))
return !sMatchFlag(s,pkg);
if(Consume(s,"(")) {
bool b = sMatchOr(s,pkg);
Consume(s,")");
return b;
}
if(s.IsEmpty())
return true;
int len=0;
while(IsId(s[len]))
len++;
String id = s.Mid(0,len);
s = s.Mid(len);
if(flags.Find(id)>=0) return true;
for(int j=0;j<dflags.GetCount();j++)
if((pkg==0||pkgs[pkg].accepts.Find(dflags[j])>=0) && dflags.Find(id)>=0)
return true;
if(pkg==0&&id=="MAIN") return true;
return false;
}
bool sMatchAnd(String& s,int pkg){
bool b = sMatchFlag(s,pkg);
while( IsId(s[0]) || s[0]=='!' || s[0]=='(' || Consume(s,"&&") || Consume(s,"&"))
b = sMatchFlag(s,pkg) && b;
return b;
}
bool sMatchOr(String& s,int pkg){
bool b = sMatchAnd(s,pkg);
while(Consume(s,"||") || Consume(s,"|"))
b = sMatchFlag(s,pkg) || b;
return b;
}
bool EvalOpt(const OptItem& opt,int pkg){
if(opt.state!=UNKNOWN)
return opt.state==YES;
String s;
for(const char* c = opt.when; *c; c++)
if(*c > ' ') s += *c;
if(s=="()"){
opt.state=YES;
return true;
}
bool b = sMatchOr(s,pkg);
if(!s.IsEmpty()){
fprintf(stderr, "Error: Invalid WHEN string: %s\n",~opt.when);
exit(1);
}
opt.state=b?YES:NO;
return b;
}
void GetDeps(const String& pkg){
if(pkgs.Find(Upp(pkg))>=0)
return;
Upp& added = pkgs.Add().Load(pkg, nests);
Vector<String> u;
for(int i=0;i<added.uses.GetCount();i++)
if(EvalOpt(added.uses[i],pkgs.GetCount()-1))
u.Add(added.uses[i].item);
for(int i=0;i<u.GetCount();i++)
GetDeps(u[i]);
}
String EvalVector(const Vector<OptItem>& v,int pkg,const String& prefix){
String r;
for(int i=0;i<v.GetCount();i++){
if(EvalOpt(v[i],pkg))
r+=" "+prefix+v[i].item;
}
return r;
}
void EvalFlags(){
String umc = GetEnv("USEMAINCFG","1");
int n;
sscanf(~umc,"%d",&n);
if(pkgs[0].cfgk.GetCount()==0)
fprintf(stderr,"Warning: %s doesn't seem to be a main package.\n", ~pkgs[0].name);
String p;
bool interactive = GetEnv("INTERACTIVE","0")=="1";
if (!interactive) {
n=n?n-1:-1;
} else if(interactive){
if (pkgs[0].cfgk.GetCount()){
printf("Multiple configurations available, select one\n");
int i=0;
for(;i<pkgs[0].cfgk.GetCount();i++)
printf(" %i) '%s': '%s'\n",i+1,~pkgs[0].cfgk[i],~pkgs[0].cfgv[i]);
printf(" *) Custom flags (type in as space separated list)\n");
printf("Your selection [%d]:\n",n);
} else {
printf("Add custom flags (as space separated list) [\"\"]:\n");
}
fflush(0);
char buf[256];
fgets(buf, 256, stdin);
if (strlen(buf) == 0 || sscanf(buf, "%d", &n)) {
n--;
} else {
p = buf;
n = -1;
}
}
if(n >= 0 && n < pkgs[0].cfgk.GetCount())
p = pkgs[0].cfgv[n];
if(p)
Split(p,flags);
p="Using flags";
for(int i=0; i<flags.GetCount(); i++)
p+=" "+flags[i];
printf("%s\n",~p);
Vector<String> dflags;
for(int i=0; i<flags.GetCount(); i++)
if(flags[i][0]=='.'){
dflags.Add(flags[i].Mid(1));
}
pkgs[0].flags.Add("MAIN");
for(int i=0;i<pkgs.GetCount();i++){
for(int j=0;j<dflags.GetCount();j++)
if(i==0 || pkgs[i].accepts.Find(dflags[j])>=0)
pkgs[i].flags.Add(dflags[j]);
for(int j=0;j<pkgs[i].addflags.GetCount();j++)
EvalOpt(pkgs[i].addflags[j],i);
Vector<String> remflags;
for(int j=0;j<pkgs[i].addflags.GetCount();j++){
if(EvalOpt(pkgs[i].addflags[j],i))
if(pkgs[i].addflags[j].item[0]=='!')
Split(pkgs[i].addflags[j].item.Mid(1), remflags);
else
Split(pkgs[i].addflags[j].item, pkgs[i].flags);
}
for(int j=0;j<flags.GetCount();j++){
if(flags[j][0]=='.' || remflags.Find(flags[j])>=0) continue;
pkgs[i].flags.Add(flags[j]);
}
}
}
void EvalOpts(){
String optimize=GetEnv("OPT");
for(int i=0;i<pkgs.GetCount();i++){
if(pkgs[i].opt==NONE && optimize.GetCount()){
if (optimize=="SPEED")
pkgs[i].opt=SPEED;
else if (optimize=="SIZE")
pkgs[i].opt=SIZE;
}
for(int j=0;j<pkgs[i].library.GetCount();j++){
if(EvalOpt(pkgs[i].library[j],i))
for(String s=pkgs[i].library[j].item;s.GetCount();){
String v=ReadValue(s,' ');
if(v=="" || v==" ") break;
libs.AddUnique(v);
}
}
for(int j=0;j<pkgs[i].link.GetCount();j++)
if(EvalOpt(pkgs[i].link[j],i))
linkopts.Add(pkgs[i].link[j].item);
}
}
Parser(const char* mainpkg) : main(mainpkg){
Split(GetEnv("NESTS","uppsrc bazaar reference tutorial examples /home/h/MyApps"), nests);
Split(GetEnv("FLAGS","GCC GUI"), flags);
Split(GetEnv("PLATFORM","POSIX LINUX"), flags);
GetDeps(main);
EvalFlags();
EvalOpts();
Process();
}
void Process();
};
#include PROCESS
int main(int argc, const char *argv[]){
if(argc != 2){
fprintf(stderr, "Usage: %s package\n", argv[0]);
return 1;
}
Parser r(argv[1]);
return 0;
}

View file

@ -0,0 +1,26 @@
/*
This is an example file. Anyone can use it as a base for
additional parser output formats.
*/
#define DUMP(X) \
printf(#X":\n");\
for(int q = 0; q < X.GetCount(); q++) \
printf(" [%i] = %s\n", q, ~X[q]);
void Parser::Process(){
DUMP(flags);
DUMP(nests);
printf("packages:\n");
for(int i = 0; i < pkgs.GetCount(); i++){
printf(" %s\n", ~pkgs[i].name);
for(int j = 0; j < pkgs[i].flags.GetCount(); j++){
printf("%s%s", j ?", ":" ", ~pkgs[i].flags[j]);
}
printf("\n");
for(int j = 0; j < pkgs[i].custom.GetCount(); j++){
printf(" %s%s\n", ~pkgs[i].custom[j].when, ~pkgs[i].custom[j].item);
}
printf("\n");
}
}

View file

@ -0,0 +1,30 @@
options(MAKE) -DPROCESS="<UppBuilder/Make.hpp>";
options(TEST) -DPROCESS="<UppBuilder/Skeleton.hpp>";
file
Parser.cpp,
Make readonly separator,
Make.hpp,
update.sh,
Makefile.in highlight sh,
color.sh,
brc.sh,
Test readonly separator,
Skeleton.hpp,
Doc readonly separator,
srcdoc.tpp,
src.tpp;
mainconfig
"" = "MAKE",
"" = "TEST";
custom() ".hpp",
"sh -c \"if [ Parser.cpp -ot $(FILE) ]; then touch Parser.cpp; fi;\"",
"PHONY";
custom(MAKE) ".in",
"sh update.sh",
"Makefile";

66
uppbox/UppBuilder/brc.sh Normal file
View file

@ -0,0 +1,66 @@
#!/bin/sh
# this script converts .brc file into .c file suitable for compilation
# usage: brc.sh file.brc > output.c
set -e
cd "`dirname $1`"
f="`basename $1`"
tmp="`mktemp -d brc_XXXXXX`"
sed 's/[(),]/ /g;s/\r//' $f > $tmp/preparsed
toBin() {
case $2 in
ZIP) obj=$tmp/obj.gz; cat "$1" | gzip -9 > $obj ;;
BZ2) obj=$tmp/obj.bz2; cat "$1" | bzip2 -9 > $obj ;;
*) obj="$1" ;;
esac
hexdump -e '"\t" 16/1 "%i, " "\n"' $obj | sed '$s/[ ,]\+$/, 0/g'
len="`cat $obj | wc -c`"
}
BINARY() {
echo "static char $1_[] = {"
toBin "$2" $3
echo "};"
echo "char *$1 = $1_;"
echo "int $1_length = $len;";
}
BINARY_MASK() {
c=0
for file in $2; do
BINARY_ARRAY $1 $c $file $3
c=$(( $c + 1 ))
files="$files\n\t\"$file\","
done
echo -e "char *bin_all_files[] = {$files\n};"
}
BINARY_ARRAY() {
echo "static char $1_$2[] = {"
toBin "$3" $4
echo "};"
[ -z "`echo $arrays | grep $1`" ] && arrays="$1 $arrays"
echo "$1_$2 $len" >> $tmp/len
}
. $tmp/preparsed | tee $tmp/out
for a in $arrays; do
c="`grep -c $a'_[0-9]\+' $tmp/out`"
echo "int $a""_count = $c;"
echo "int $a""_length[] = {"
n=0
while [ $n -lt $c ]; do
sed -n "/$a""_$n"' /s/.* \([0-9]*\)/\t\1,/p' $tmp/len
n=$(( $n + 1 ))
done
echo "};"
echo "char *$a[] = { "
grep -o $a'_[0-9]\+' $tmp/out | sed 's/^/\t/;s/$/,/;'
echo " };"
done
rm -r $tmp

View file

@ -0,0 +1,14 @@
#!/bin/sh
s='s/error:/\x1B[1;31m&\x1B[0m/ig;
s/warning:/\x1B[1;35m&\x1B[0m/ig;
s/note:/\x1B[1;32m&\x1B[0m/ig;
s/info:/\x1B[1;32m&\x1B[0m/ig;'
# redirection magic makes the sed command work on stdout
# as well as on stderr, without mixing them up
f="`mktemp -t status.XXXXXX`"
{ { {
$@
} ; echo $? > $f; } 3>&1 1>&2 2>&3 | sed "$s"; } 3>&1 1>&2 2>&3 | sed "$s"
read ret < $f
rm -f $f
exit $ret

3
uppbox/UppBuilder/init Normal file
View file

@ -0,0 +1,3 @@
#ifndef _UppBuilder_icpp_init_stub
#define _UppBuilder_icpp_init_stub
#endif

View file

@ -0,0 +1,45 @@
topic "miscellaneous structs";
[ $$0,0#00000000000000000000000000000000:Default]
[i448;a25;kKO9; $$1,0#37138531426314131252341829483380:structitem]
[l288;2 $$2,0#27521748481378242620020725143825:desc]
[0 $$3,0#96390100711032703541132217272105:end]
[H6;0 $$4,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:codeitem]
[{_}
[s1;:File`:`:struct: [@(0.0.255) struct]_[* File]&]
[s2;%% Class holding information about file that should be compiled.&]
[s3; &]
[s4; &]
[s5;:File`:`:opt: Opt_[* opt]&]
[s2;%% Speed or size optimization flag&]
[s3; &]
[s4; &]
[s5;:File`:`:name: [_^String^ String]_[* name]&]
[s2;%% Filename, as specified in .upp file&]
[s3; &]
[s4; &]
[s5;:File`:`:lang: [_^String^ String]_[* lang]&]
[s2;%% Programming language, one of cxx, c, brc, guessed by extension.&]
[s3; &]
[s4; &]
[s3; &]
[s0; &]
[s3; &]
[s1;:OptItem`:`:struct: [@(0.0.255) struct]_[* OptItem]&]
[s2;%% Class holding any information from .upp file that has WHEN
clause.&]
[s0;%% &]
[s4; &]
[s5;:OptItem`:`:when: [_^String^ String]_[* when]&]
[s2;%% When clause&]
[s3; &]
[s4; &]
[s5;:OptItem`:`:item: [_^String^ String]_[* item]&]
[s2;%% The value&]
[s3; &]
[s4; &]
[s5;:OptItem`:`:state: [@(0.0.255) mutable]_OptState_[* state]&]
[s2;%% UNKNOWN if the clause wasn`'t evaluated yet, otherwise YES
or NO, depending on the evaluation result.&]
[s3; &]
[s0;%% ]

View file

@ -0,0 +1,31 @@
topic "class Parser";
[ $$0,0#00000000000000000000000000000000:Default]
[i448;a25;kKO9; $$1,0#37138531426314131252341829483380:structitem]
[l288;2 $$2,0#27521748481378242620020725143825:desc]
[0 $$3,0#96390100711032703541132217272105:end]
[H6;0 $$4,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:codeitem]
[{_}
[s1;:Parser`:`:class: [@(0.0.255) class]_[* Parser]&]
[s2;%% Main class that does all the work: loads the main package and
all its dependencies, evaluates the contents and outputs the
results. The output writer is interchangeable to allow generating
information to various build systems.&]
[s3; &]
[s4; &]
[s5;:Parser`:`:Parser`(const char`*`): [* Parser]([@(0.0.255) const]_[@(0.0.255) char`*]_[*@3 m
ainpkg])&]
[s2;%% Constructor, the parameter [%-*@3 mainpkg] specifies which package
should be parsed, everything else is determined from [^topic`:`/`/UppBuilder`/srcdoc`/environment`$en`-us^ e
nvironment variables]&]
[s3;%% &]
[s4; &]
[s5;:Parser`:`:Process`(`): [@(0.0.255) void]_[* Process]()&]
[s2;%% The output function, called when everything is parsed. It
can access all the information gathered in the parsing stage
in order to do whatever necessary to build the package. This
function is supplied by backend via macro definition, so it can
do exactly what the backend needs, e.g. generate makefile or
even call the compiler itself. &]
[s3; &]
[s0;%% ]

View file

@ -0,0 +1,112 @@
topic "class String";
[ $$0,0#00000000000000000000000000000000:Default]
[i448;a25;kKO9; $$1,0#37138531426314131252341829483380:structitem]
[l288;2 $$2,0#27521748481378242620020725143825:desc]
[0 $$3,0#96390100711032703541132217272105:end]
[H6;0 $$4,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:codeitem]
[{_}
[s1;:String`:`:class: [@(0.0.255) class]_[* String]&]
[s2;%% Class implementing a very basic String. The idea behind reimplementing
such basic thing is to avoid including a huge portion of U`+`+/STL,
which slows down compilation and also add dependencies.&]
[s3; &]
[s4; &]
[s5;:String`:`:String`(`): [* String]()&]
[s2;%% Default constructor&]
[s3; &]
[s4; &]
[s5;:String`:`:String`(const String`&`): [* String]([@(0.0.255) const]_[* String][@(0.0.255) `&
]_[*@3 s])&]
[s5;:String`:`:String`(const char`*`): [* String]([@(0.0.255) const]_[@(0.0.255) char`*]_[*@3 s
])&]
[s2;%% Copy constructors&]
[s3;%% &]
[s4; &]
[s5;:String`:`:`~String`(`): [@(0.0.255) `~][* String]()&]
[s2;%% Destructor&]
[s3; &]
[s4; &]
[s5;:String`:`:operator`[`]`(const int`)const: [@(0.0.255) char`&]_[* operator`[`]]([@(0.0.255) c
onst]_[@(0.0.255) int]_[*@3 n])_[@(0.0.255) const]&]
[s2;%% Returns [%-*@3 n]th character&]
[s3;%% &]
[s4; &]
[s5;:String`:`:IsEmpty`(`)const: [@(0.0.255) bool]_[* IsEmpty]()_[@(0.0.255) const]&]
[s2;%% Returns true if the String is empty&]
[s3; &]
[s4; &]
[s5;:String`:`:operator`~`(`)const: [@(0.0.255) const]_[@(0.0.255) char`*]_[* operator`~]()
_[@(0.0.255) const]&]
[s2;%% Returns pointer to internal char`* representation, similar
to std`::string.c`_str()&]
[s3; &]
[s4; &]
[s5;:String`:`:operator const char`*`(`)const: [* operator_const_char`*]()_[@(0.0.255) co
nst]&]
[s2;%% Converts String to const char`*&]
[s3; &]
[s4; &]
[s5;:String`:`:operator`=`(const String`&`): [_^String^ String][@(0.0.255) `&]_[* operator`=
]([@(0.0.255) const]_[_^String^ String][@(0.0.255) `&]_[*@3 s])&]
[s5;:String`:`:operator`=`(const char`*`): [_^String^ String][@(0.0.255) `&]_[* operator`=](
[@(0.0.255) const]_[@(0.0.255) char`*]_[*@3 s])&]
[s2;%% Assignment operators&]
[s3;%% &]
[s4; &]
[s5;:String`:`:operator`=`=`(const char`*`)const: [@(0.0.255) bool]_[* operator`=`=]([@(0.0.255) c
onst]_[@(0.0.255) char`*]_[*@3 s])_[@(0.0.255) const]&]
[s5;:String`:`:operator`=`=`(const String`&`)const: [@(0.0.255) bool]_[* operator`=`=]([@(0.0.255) c
onst]_[_^String^ String][@(0.0.255) `&]_[*@3 s])_[@(0.0.255) const]&]
[s2;%% Compares string with [%-*@3 s].&]
[s3; &]
[s4; &]
[s5;:String`:`:Clear`(`): [@(0.0.255) void]_[* Clear]()&]
[s2;%% Empties the String&]
[s3; &]
[s4; &]
[s5;:String`:`:GetCount`(`)const: [@(0.0.255) int]_[* GetCount]()_[@(0.0.255) const]&]
[s2;%% Returns number of characters stored&]
[s3; &]
[s4; &]
[s5;:String`:`:operator`+`=`(const String`&`): [_^String^ String][@(0.0.255) `&]_[* operato
r`+`=]([@(0.0.255) const]_[_^String^ String][@(0.0.255) `&]_[*@3 s])&]
[s5;:String`:`:operator`+`=`(char`): [_^String^ String][@(0.0.255) `&]_[* operator`+`=]([@(0.0.255) c
har]_[*@3 s])&]
[s2;%% Adds [%-*@3 s] at the end of the String&]
[s3; &]
[s4; &]
[s5;:String`:`:Compare`(const char`*`,int`)const: [@(0.0.255) int]_[* Compare]([@(0.0.255) c
onst]_[@(0.0.255) char`*]_[*@3 s], [@(0.0.255) int]_[*@3 len][@(0.0.255) `=`-][@3 1])_[@(0.0.255) c
onst]&]
[s2;%% Compares the string to [%-*@3 s]. If [%-*@3 len] > 0, then only
first [%-*@3 len] characters is considered.&]
[s3;%% &]
[s4; &]
[s5;:String`:`:FindLast`(const char`)const: [@(0.0.255) int]_[* FindLast]([@(0.0.255) const
]_[@(0.0.255) char]_[*@3 c])_[@(0.0.255) const]&]
[s2;%% Find last occurrence of character [%-*@3 c].&]
[s4; &]
[s5;:String`:`:Find`(const char`*`)const: [@(0.0.255) int]_[* Find]([@(0.0.255) const]_[@(0.0.255) c
har`*]_[*@3 s])_[@(0.0.255) const]&]
[s2;%% Find first occurrence of string [%-*@3 s].&]
[s3;%% &]
[s4; &]
[s5;:String`:`:Mid`(const int`,const int`)const: [_^String^ String]_[* Mid]([@(0.0.255) con
st]_[@(0.0.255) int]_[*@3 start], [@(0.0.255) const]_[@(0.0.255) int]_[*@3 length][@(0.0.255) `=
`-][@3 1])_[@(0.0.255) const]&]
[s2;%% Returns a substring starting at [%-*@3 start]. If [%-*@3 length]
> 0 the returned string si truncated to the [%-*@3 length] characters.&]
[s3;%% &]
[s4; &]
[s5;:operator`+`(const String`&`,const String`&`): [_^String^ String]_[* operator`+]([@(0.0.255) c
onst]_[_^String^ String][@(0.0.255) `&]_[*@3 lhs], [@(0.0.255) const]_[_^String^ String][@(0.0.255) `&
]_[*@3 rhs])&]
[s2;%% Concatenates [%-*@3 lhs] and [%-*@3 rhs].&]
[s3;%% &]
[s4; &]
[s5;:operator`<`(const String`&`,const String`&`): [@(0.0.255) int]_[* operator<]([@(0.0.255) c
onst]_[_^String^ String][@(0.0.255) `&]_[*@3 lhs], [@(0.0.255) const]_[_^String^ String][@(0.0.255) `&
]_[*@3 rhs])&]
[s2;%% Compares [%-*@3 lhs] and [%-*@3 rhs].&]
[s0;%% ]

View file

@ -0,0 +1,19 @@
topic "struct Upp";
[ $$0,0#00000000000000000000000000000000:Default]
[i448;a25;kKO9; $$1,0#37138531426314131252341829483380:structitem]
[l288;2 $$2,0#27521748481378242620020725143825:desc]
[0 $$3,0#96390100711032703541132217272105:end]
[H6;0 $$4,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:codeitem]
[{_}
[s1;:Upp`:`:struct: [@(0.0.255) struct]_[* Upp]&]
[s2;%% Struct representing all data found in .upp file&]
[s3; &]
[s4; &]
[s5;:Upp`:`:Load`(const String`&`,const Vector`<String`>`&`): [_^Upp^ Upp][@(0.0.255) `&]_
[* Load]([@(0.0.255) const]_[_^String^ String][@(0.0.255) `&]_[*@3 package],[@(0.0.255) const
]_[_^Vector^ Vector]<[_^String^ String]>`&_[*@3 nests])&]
[s2;%% Finds [%-*@3 package] in [%-*@3 nests] and reads all information
from its .upp file&]
[s3;%% &]
[s0;%% ]

View file

@ -0,0 +1,63 @@
topic "class Vector";
[ $$0,0#00000000000000000000000000000000:Default]
[i448;a25;kKO9; $$1,0#37138531426314131252341829483380:structitem]
[l288;2 $$2,0#27521748481378242620020725143825:desc]
[0 $$3,0#96390100711032703541132217272105:end]
[H6;0 $$4,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$5,0#37138531426314131252341829483370:codeitem]
[{_}
[s1;:noref: [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T]>&]
[s1;:Vector`:`:class: [@(0.0.255) class]_[* Vector]&]
[s2;%% Class template implementing a very basic Vector container.
The idea behind reimplementing such basic thing is to avoid including
a huge portion of U`+`+/STL, which slows down compilation and
also add dependencies.&]
[s4; &]
[s5;:Vector`:`:Vector`(`): [* Vector]()&]
[s2;%% Default constructor&]
[s3; &]
[s4; &]
[s5;:Vector`:`:`~Vector`(`): [@(0.0.255) `~][* Vector]()&]
[s2;%% Destructor&]
[s3; &]
[s4; &]
[s5;:Vector`:`:GetCount`(`)const: [@(0.0.255) int]_[* GetCount]()[@(0.0.255) const]&]
[s2;%% Returns the number of elements stored&]
[s3; &]
[s4; &]
[s5;:Vector`:`:operator`[`]`(int`): [*@4 T][@(0.0.255) `&]_[* operator`[`]]([@(0.0.255) int]_
[*@3 n])&]
[s2;%% Returns reference to element stored at [%-*@3 n]th position&]
[s3;%% &]
[s4; &]
[s5;:Vector`:`:operator`[`]`(int`)const: [@(0.0.255) const]_[*@4 T][@(0.0.255) `&]_[* operato
r`[`]]([@(0.0.255) int]_[*@3 n])_[@(0.0.255) const]&]
[s2;%% Returns const reference to element stored at [%-*@3 n]th position&]
[s3;%% &]
[s4; &]
[s5;:Vector`:`:Add`(`): [*@4 T][@(0.0.255) `&]_[* Add]()&]
[s2;%% Adds default constructed element at the end of the vector&]
[s3; &]
[s4; &]
[s5;:Vector`:`:Add`(const T`&`): [*@4 T][@(0.0.255) `&]_[* Add]([@(0.0.255) const]_[*@4 T][@(0.0.255) `&
]_[*@3 x])&]
[s2;%% Adds an element copy constructed from [%-*@3 x] at the end of
the vector&]
[s3;%% &]
[s4; &]
[s5;:Vector`:`:AddUnique`(const T`&`): [*@4 T][@(0.0.255) `&]_[* AddUnique]([@(0.0.255) const
]_[*@4 T][@(0.0.255) `&]_[*@3 v])&]
[s2;%% Adds element [%-*@3 v] at the end of the vector if it doesn`'t
exist already&]
[s3;%% &]
[s4; &]
[s5;:Vector`:`:Find`(const T`&`)const: [@(0.0.255) int]_[* Find]([@(0.0.255) const]_[*@4 T][@(0.0.255) `&
]_[*@3 v])_[@(0.0.255) const]&]
[s2;%% Returns position of [%-*@3 v] in the vector, or `-1 if not present&]
[s3;%% &]
[s4; &]
[s5;:Vector`:`:Resize`(int`): [@(0.0.255) void]_[* Resize]([@(0.0.255) int]_[*@3 len])&]
[s2;%% Allocates memory for [%-*@3 len] fields. If the vector is longer,
it is truncated.&]
[s3;%% &]
[s0;%% ]

View file

@ -0,0 +1,53 @@
topic "global functions";
[ $$0,0#00000000000000000000000000000000:Default]
[H6;0 $$1,0#05600065144404261032431302351956:begin]
[i448;a25;kKO9;2 $$2,0#37138531426314131252341829483370:codeitem]
[l288;2 $$3,0#27521748481378242620020725143825:desc]
[0 $$4,0#96390100711032703541132217272105:end]
[i448;a25;kKO9; $$5,0#37138531426314131252341829483380:structitem]
[{_}
[s2;:NEVER`(T`): [@(0.0.255) template]_<[@(0.0.255) class]_[*@4 T]>_[@(0.0.255) void]_[* NEVER](
[*@4 T]_[*@3 s])&]
[s3;%% Safely terminates the parser when [%-*@3 s] evaluates to true.
Used to end the execution in case of nonrecoverable state.&]
[s4;%% &]
[s1; &]
[s2;:Slashes`(String`&`): [@(0.0.255) void]_[* Slashes]([_^String^ String][@(0.0.255) `&]_[*@3 s
])&]
[s3;%% Converts all backslashes in [%-*@3 s] to forward slashes.&]
[s4;%% &]
[s1; &]
[s2;:BaseName`(const String`&`): [_^String^ String]_[* BaseName]([@(0.0.255) const]_[_^String^ S
tring][@(0.0.255) `&]_[*@3 s])&]
[s3;%% Returns everything after last `'/`' character in [%-*@3 s].&]
[s4;%% &]
[s1; &]
[s2;:DirName`(const String`&`): [_^String^ String]_[* DirName]([@(0.0.255) const]_[_^String^ S
tring][@(0.0.255) `&]_[*@3 s])&]
[s3;%% Returns everything before last `'/`' character in [%-*@3 s].&]
[s4;%% &]
[s1; &]
[s2;:LoadFile`(const String`&`): [_^String^ String]_[* LoadFile]([@(0.0.255) const]_[_^String^ S
tring][@(0.0.255) `&]_[*@3 fn])&]
[s3;%% Loads content of file [%-*@3 fn] into string.&]
[s4;%% &]
[s1; &]
[s2;:GetEnv`(const char`*`,const char`*`): [_^String^ String]_[* GetEnv]([@(0.0.255) const]_
[@(0.0.255) char`*]_[*@3 n],[@(0.0.255) const]_[@(0.0.255) char`*]_[*@3 s][@(0.0.255) `=]`"`"
)&]
[s3;%% Reads environment variable UPP`_[%-*@3 n], if no such variable
is found then [%-*@3 s] is returned.&]
[s4;%% &]
[s1; &]
[s2;:Split`(String`,Vector`<String`>`&`): [@(0.0.255) void]_[* Split]([_^String^ String]_[*@3 s
],[_^Vector^ Vector]<[_^String^ String]>`&_[*@3 v])&]
[s3;%% Splits string [%-*@3 s] at spaces and adds each chunk into vector
[%-*@3 v].&]
[s4;%% &]
[s1; &]
[s2;:FlagDir`(const Vector`<String`>`&`): [_^String^ String]_[* FlagDir]([@(0.0.255) const]_
[_^Vector^ Vector]<[_^String^ String]>`&_[*@3 v])&]
[s3;%% Sorts contents of [%-*@3 v] alphabetically and concatenates
it into string with `'.`' as delimiters&]
[s4;%% &]
[s0;%% ]

View file

@ -0,0 +1,18 @@
topic "TODO list";
[ $$0,0#00000000000000000000000000000000:Default]
[{_}%EN-US
[s0; Parser:&]
[s0;i150;O0; Finish custom build steps support&]
[s0;i150;O0; Separate parsing from output&]
[s0;i150;O0; Pretify (remove useless variables, remove DBGs, ...)&]
[s0;i150;O0; Add comments&]
[s0;i150;O0; Extend interactive mode&]
[s0; &]
[s0; Generated makefile:&]
[s0;i150;O0; Simplify and shorten&]
[s0; &]
[s0; Ideas for future:&]
[s0;i150;O0; Shell backend `- building without make&]
[s0;i150;O0; other common build systems `- cmake, scons, waf, ...&]
[s0;i150;O0; BLITZ support&]
[s0; ]

View file

@ -0,0 +1,46 @@
topic "Dependencies";
[2 $$0,0#00000000000000000000000000000000:Default]
[l288;i1120;a17;O9;~~~.1408;2 $$1,0#10431211400427159095818037425705:param]
[a83;*R6 $$2,5#31310162474203024125188417583966:caption]
[H4;b83;*4 $$3,5#07864147445237544204411237157677:title]
[i288;O9;C2 $$4,6#40027414424643823182269349404212:item]
[b42;a42;2 $$5,5#45413000475342174754091244180557:text]
[l288;b17;a17;2 $$6,6#27521748481378242620020725143825:desc]
[l321;C@5;1 $$7,7#20902679421464641399138805415013:code]
[b2503;2 $$8,0#65142375456100023862071332075487:separator]
[*@(0.0.255)2 $$9,0#83433469410354161042741608181528:base]
[C2 $$10,0#37138531426314131251341829483380:class]
[l288;a17;*1 $$11,11#70004532496200323422659154056402:requirement]
[i417;b42;a42;O9;~~~.416;2 $$12,12#10566046415157235020018451313112:tparam]
[b167;C2 $$13,13#92430459443460461911108080531343:item1]
[i288;a42;O9;C2 $$14,14#77422149456609303542238260500223:item2]
[*@2$(0.128.128)2 $$15,15#34511555403152284025741354420178:NewsDate]
[l321;*C$7;2 $$16,16#03451589433145915344929335295360:result]
[l321;b83;a83;*C$7;2 $$17,17#07531550463529505371228428965313:result`-line]
[l160;*C+117 $$18,5#88603949442205825958800053222425:package`-title]
[2 $$19,0#53580023442335529039900623488521:gap]
[C2 $$20,20#70211524482531209251820423858195:class`-nested]
[b50;2 $$21,21#03324558446220344731010354752573:Par]
[{_}%EN-US
[s3; Dependencies&]
[s5; &]
[s5; The Makefile produced by this package when MAKE flag is specified
aims to be as universal as possible, however it still depends
on couple programs:&]
[s5;i150;O0; make `- should POSIX compatible (see [^http`:`/`/pubs`.opengroup`.org`/onlinepubs`/009695399`/utilities`/make`.html^ h
ttp://pubs.opengroup.org/onlinepubs/009695399/utilities/make.html]),
GNU make and remake were tested&]
[s5;i150;O0; sed `- again POSIX compatible (see [^http`:`/`/pubs`.opengroup`.org`/onlinepubs`/009695399`/utilities`/sed`.html^ h
ttp://pubs.opengroup.org/onlinepubs/009695399/utilities/sed.html])&]
[s5;i150;O0; gzip&]
[s5;i150;O0; base64&]
[s5; Additionaly, for projects using BRC files:&]
[s5;i150;O0; grep&]
[s5;i150;O0; hexdump&]
[s5;i150;O0; bzip2 (only for BRC files that contain bzip2 compressed
resources)&]
[s5;i150;O0; couple standard utilities found on any system: dirname,
basename, mktemp and tee&]
[s5; &]
[s5; ... and of course a compiler (GCC and Clang tested), ar and
linker to build the executables]

View file

@ -0,0 +1,80 @@
topic "Environment variables";
[ $$0,0#00000000000000000000000000000000:Default]
[{_}%EN-US
[s0; The parser is influenced by several environment variables. &]
[s0; &]
[s0; Parser:&]
[ {{2738:1920:5342h1;^ [s0; [* Variable]]
:: [s0; [* Default]]
:: [s0; [* Meaning]]
:: [s0; UPP`_NESTS]
:: [s0; uppsrc bazaar reference tutorial examples]
:: [s0; Where to look for packages]
:: [s0; UPP`_FLAGS]
:: [s0; GCC GUI]
:: [s0; Build flags, same meaning as in theide]
:: [s0; UPP`_PLATFORM]
:: [s0; POSIX LINUX]
:: [s0; Additional flags describing platform]
:: [s0; UPP`_OPT]
:: [s0; ]
:: [s0; SPEED or SIZE to build optimized for speed or size respectively]
:: [s0; UPP`_USEMAINCFG]
:: [s0; 1]
:: [s0; Whether to add flags from based on mainconfig section (1`-based
index) or 0 to disable this feature]
:: [s0; UPP`_INTERACTIVE]
:: [s0; 0]
:: [s0; Whether the parser can ask for some ]}}&]
[s0; &]
[s0; &]
[s0; Make backend:&]
[ {{2451:4582:2967h1;^ [s0; [* Variable]]
:: [s0; [* Default]]
::= [s0; [* Meaning]]
::^ [s0; UPP`_OUT]
:: [s0; /tmp/`_out]
::=|14 [s0; See explanation of corresponding variables in Makefile.in ]
::^ [s0; UPP`_BIN]
:: [s0; `$(OUT)/bin]
::= [s0;%- ]
::^ [s0; UPP`_CFLAGS]
:: [s0; `-O2]
::= [s0;%- ]
::^ [s0; UPP`_CXXFLAGS]
:: [s0; `$(CFLAGS)]
::= [s0;%- ]
::^ [s0; UPP`_LDFLAGS]
:: [s0; `-Wl,`-`-gc`-sections `-Wl,`-s `-Wl,`-O,2]
::= [s0;%- ]
::^ [s0; UPP`_SPEEDFLAGS]
:: [s0; `-O3 `-ffunction`-sections `-fdata`-sections]
::= [s0;%- ]
::^ [s0; UPP`_SIZEFLAGS]
:: [s0; `-Os `-finline`-limit`=20 `-ffunction`-sections `-fdata`-sections]
::= [s0;%- ]
::^ [s0; UPP`_INCPATHS]
:: [s0; ]
::= [s0;%- ]
::^ [s0; UPP`_LIBPATHS]
:: [s0; ]
::= [s0;%- ]
::^ [s0; UPP`_CXX]
:: [s0; `$(CXX)]
::= [s0;%- ]
::^ [s0; UPP`_CC]
:: [s0; `$(CC)]
::= [s0;%- ]
::^ [s0; UPP`_AR]
:: [s0; ar `-src]
::= [s0;%- ]
::^ [s0; UPP`_DEPS]
:: [s0; 0]
::= [s0;%- ]
::^ [s0; UPP`_ECHO]
:: [s0; 0]
::= [s0;%- ]
::^ [s0; UPP`_SILENT]
:: [s0; 0]
::= [s0;%- ]}}&]
[s0; ]

View file

@ -0,0 +1,45 @@
#!/bin/sh
# This script compresses the parser and embeds it into
# the Makefile, using Makefile.in template
echo Updating Makefile
# output file can be overriden by MAKEFILE_OUT variable
[ "$MAKEFILE_OUT" ] || MAKEFILE_OUT="/tmp/Makefile"
{
cat Makefile.in
echo '### PARSER CODE (gzipped and base64 encoded) ###'
{
#we perform the '#include' first
sed '/#include PROCESS/,$d' Parser.cpp
cat Make.hpp
sed '1,/#include PROCESS/d' Parser.cpp
} | gzip -9 | base64 -w74 | sed 's/.*/#:&/;'
echo '### BRC parser (gzipped and base64 encoded) ###'
cat brc.sh | gzip -9 | base64 -w74 | sed 's/.*/#~&/;'
echo '### Colorizer (gzipped and base64 encoded) ###'
cat color.sh | gzip -9 | base64 -w74 | sed 's/.*/#%&/;'
} > "$MAKEFILE_OUT"
# little trick to ensure Makefile is always "rebuild"
sh -c 'sleep 2 && touch Makefile.in'&
exit 0 # comment this line out to see compression statistics
stat() {
printf "%s %5i -> %5i %3i -> %3i\n" "$1"\
`cat $2 | wc -c` `grep $3 "$MAKEFILE_OUT" | wc -c` \
`cat $2 | wc -l` `grep $3 "$MAKEFILE_OUT" | wc -l`
}
echo "Compression info:"
printf "%s%12s%14s\n" "File " bytes lines
stat "Parser " Parser.cpp '^#:'
stat "Brc parser" brc.sh '^#~'
stat "Colorizer " color.sh '^#%'
stat "Total " "Parser.cpp brc.sh color.sh" '^#[%~:]'
stat "Makefile " Makefile.in '^'