aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Buland <eichlan@xagasoft.com>2009-12-21 18:04:02 +0000
committerMike Buland <eichlan@xagasoft.com>2009-12-21 18:04:02 +0000
commitfb28f6800864176be2ffca29e8e664b641f33170 (patch)
treeba9180ac442939edc4eacbe1fdae93c5a7f87cee
parent51e21a316be6e052251b3dfc7d671061ebd67cee (diff)
downloadbuild-fb28f6800864176be2ffca29e8e664b641f33170.tar.gz
build-fb28f6800864176be2ffca29e8e664b641f33170.tar.bz2
build-fb28f6800864176be2ffca29e8e664b641f33170.tar.xz
build-fb28f6800864176be2ffca29e8e664b641f33170.zip
m3 is copied into trunk, we should be good to go, now.
Diffstat (limited to '')
-rw-r--r--Doxyfile275
-rw-r--r--build.conf35
-rw-r--r--default.bld129
-rw-r--r--docs/build.15
-rw-r--r--docs/build.784
-rw-r--r--docs/buildm3notes.txt99
-rw-r--r--share/autoinclude/general-rules.bld64
-rw-r--r--share/include/qt4.bld96
-rw-r--r--src/action.cpp107
-rw-r--r--src/action.h26
-rw-r--r--src/ast.cpp98
-rw-r--r--src/ast.h44
-rw-r--r--src/astbranch.cpp44
-rw-r--r--src/astbranch.h27
-rw-r--r--src/astleaf.cpp137
-rw-r--r--src/astleaf.h41
-rw-r--r--src/astnode.cpp113
-rw-r--r--src/astnode.h108
-rw-r--r--src/build.l216
-rw-r--r--src/build.y741
-rw-r--r--src/buildparser.cpp127
-rw-r--r--src/buildparser.h40
-rw-r--r--src/condition.cpp10
-rw-r--r--src/condition.h16
-rw-r--r--src/conditionalways.cpp21
-rw-r--r--src/conditionalways.h16
-rw-r--r--src/conditionfiletime.cpp73
-rw-r--r--src/conditionfiletime.h16
-rw-r--r--src/conditionnever.cpp21
-rw-r--r--src/conditionnever.h16
-rw-r--r--src/context.cpp524
-rw-r--r--src/context.h101
-rw-r--r--src/function.cpp16
-rw-r--r--src/function.h23
-rw-r--r--src/functionast.cpp33
-rw-r--r--src/functionast.h21
-rw-r--r--src/functiondirname.cpp38
-rw-r--r--src/functiondirname.h17
-rw-r--r--src/functiondirs.cpp61
-rw-r--r--src/functiondirs.h17
-rw-r--r--src/functionexecute.cpp68
-rw-r--r--src/functionexecute.h16
-rw-r--r--src/functionexists.cpp34
-rw-r--r--src/functionexists.h17
-rw-r--r--src/functionfilename.cpp36
-rw-r--r--src/functionfilename.h17
-rw-r--r--src/functionfiles.cpp61
-rw-r--r--src/functionfiles.h17
-rw-r--r--src/functiongetmakedeps.cpp59
-rw-r--r--src/functiongetmakedeps.h16
-rw-r--r--src/functionmatches.cpp141
-rw-r--r--src/functionmatches.h23
-rw-r--r--src/functionreplace.cpp47
-rw-r--r--src/functionreplace.h16
-rw-r--r--src/functiontargets.cpp39
-rw-r--r--src/functiontargets.h16
-rw-r--r--src/functiontostring.cpp50
-rw-r--r--src/functiontostring.h16
-rw-r--r--src/functionunlink.cpp51
-rw-r--r--src/functionunlink.h16
-rw-r--r--src/main.cpp255
-rw-r--r--src/profile.cpp116
-rw-r--r--src/profile.h30
-rw-r--r--src/rule.cpp167
-rw-r--r--src/rule.h53
-rw-r--r--src/runner.cpp922
-rw-r--r--src/runner.h43
-rw-r--r--src/target.cpp402
-rw-r--r--src/target.h89
-rw-r--r--src/types.h26
-rw-r--r--src/variable.cpp892
-rw-r--r--src/variable.h115
-rw-r--r--src/view.cpp10
-rw-r--r--src/view.h46
-rw-r--r--src/viewdefault.cpp170
-rw-r--r--src/viewdefault.h42
-rw-r--r--src/viewmake.cpp86
-rw-r--r--src/viewmake.h36
-rw-r--r--src/viewplugger.cpp14
-rw-r--r--src/viewplugger.h20
-rw-r--r--support/vim/ftdetect/build.vim1
-rw-r--r--support/vim/ftplugin/build.vim24
-rw-r--r--support/vim/syntax/build.vim70
83 files changed, 8081 insertions, 0 deletions
diff --git a/Doxyfile b/Doxyfile
new file mode 100644
index 0000000..8bc84f8
--- /dev/null
+++ b/Doxyfile
@@ -0,0 +1,275 @@
1# Doxyfile 1.4.1-KDevelop
2
3#---------------------------------------------------------------------------
4# Project related configuration options
5#---------------------------------------------------------------------------
6PROJECT_NAME = build
7PROJECT_NUMBER = m3
8OUTPUT_DIRECTORY = api
9CREATE_SUBDIRS = NO
10OUTPUT_LANGUAGE = English
11USE_WINDOWS_ENCODING = NO
12BRIEF_MEMBER_DESC = YES
13REPEAT_BRIEF = YES
14ABBREVIATE_BRIEF = "The $name class" \
15 "The $name widget" \
16 "The $name file" \
17 is \
18 provides \
19 specifies \
20 contains \
21 represents \
22 a \
23 an \
24 the
25ALWAYS_DETAILED_SEC = NO
26INLINE_INHERITED_MEMB = NO
27FULL_PATH_NAMES = NO
28STRIP_FROM_PATH =
29STRIP_FROM_INC_PATH =
30SHORT_NAMES = NO
31JAVADOC_AUTOBRIEF = YES
32MULTILINE_CPP_IS_BRIEF = NO
33DETAILS_AT_TOP = YES
34INHERIT_DOCS = YES
35DISTRIBUTE_GROUP_DOC = NO
36TAB_SIZE = 4
37ALIASES =
38OPTIMIZE_OUTPUT_FOR_C = NO
39OPTIMIZE_OUTPUT_JAVA = NO
40SUBGROUPING = YES
41#---------------------------------------------------------------------------
42# Build related configuration options
43#---------------------------------------------------------------------------
44EXTRACT_ALL = YES
45EXTRACT_PRIVATE = YES
46EXTRACT_STATIC = YES
47EXTRACT_LOCAL_CLASSES = YES
48EXTRACT_LOCAL_METHODS = YES
49HIDE_UNDOC_MEMBERS = NO
50HIDE_UNDOC_CLASSES = NO
51HIDE_FRIEND_COMPOUNDS = NO
52HIDE_IN_BODY_DOCS = NO
53INTERNAL_DOCS = YES
54CASE_SENSE_NAMES = YES
55HIDE_SCOPE_NAMES = NO
56SHOW_INCLUDE_FILES = YES
57INLINE_INFO = YES
58SORT_MEMBER_DOCS = YES
59SORT_BRIEF_DOCS = NO
60SORT_BY_SCOPE_NAME = NO
61GENERATE_TODOLIST = YES
62GENERATE_TESTLIST = YES
63GENERATE_BUGLIST = YES
64GENERATE_DEPRECATEDLIST= YES
65ENABLED_SECTIONS =
66MAX_INITIALIZER_LINES = 30
67SHOW_USED_FILES = YES
68SHOW_DIRECTORIES = NO
69FILE_VERSION_FILTER =
70#---------------------------------------------------------------------------
71# configuration options related to warning and progress messages
72#---------------------------------------------------------------------------
73QUIET = NO
74WARNINGS = YES
75WARN_IF_UNDOCUMENTED = YES
76WARN_IF_DOC_ERROR = YES
77WARN_NO_PARAMDOC = YES
78WARN_FORMAT = "$file:$line: $text"
79WARN_LOGFILE = Doxywarn
80#---------------------------------------------------------------------------
81# configuration options related to the input files
82#---------------------------------------------------------------------------
83INPUT = src
84FILE_PATTERNS = *.c \
85 *.cc \
86 *.cxx \
87 *.cpp \
88 *.c++ \
89 *.java \
90 *.ii \
91 *.ixx \
92 *.ipp \
93 *.i++ \
94 *.inl \
95 *.h \
96 *.hh \
97 *.hxx \
98 *.hpp \
99 *.h++ \
100 *.idl \
101 *.odl \
102 *.cs \
103 *.php \
104 *.php3 \
105 *.inc \
106 *.m \
107 *.mm \
108 *.dox \
109 *.C \
110 *.CC \
111 *.C++ \
112 *.II \
113 *.I++ \
114 *.H \
115 *.HH \
116 *.H++ \
117 *.CS \
118 *.PHP \
119 *.PHP3 \
120 *.M \
121 *.MM \
122 *.C \
123 *.H \
124 *.tlh \
125 *.diff \
126 *.patch \
127 *.moc \
128 *.xpm \
129 *.dox
130RECURSIVE = YES
131EXCLUDE =
132EXCLUDE_SYMLINKS = NO
133EXCLUDE_PATTERNS =
134EXAMPLE_PATH =
135EXAMPLE_PATTERNS = *
136EXAMPLE_RECURSIVE = NO
137IMAGE_PATH =
138INPUT_FILTER =
139FILTER_PATTERNS =
140FILTER_SOURCE_FILES = NO
141#---------------------------------------------------------------------------
142# configuration options related to source browsing
143#---------------------------------------------------------------------------
144SOURCE_BROWSER = YES
145INLINE_SOURCES = NO
146STRIP_CODE_COMMENTS = YES
147REFERENCED_BY_RELATION = YES
148REFERENCES_RELATION = YES
149VERBATIM_HEADERS = YES
150#---------------------------------------------------------------------------
151# configuration options related to the alphabetical class index
152#---------------------------------------------------------------------------
153ALPHABETICAL_INDEX = YES
154COLS_IN_ALPHA_INDEX = 4
155IGNORE_PREFIX =
156#---------------------------------------------------------------------------
157# configuration options related to the HTML output
158#---------------------------------------------------------------------------
159GENERATE_HTML = YES
160HTML_OUTPUT = html
161HTML_FILE_EXTENSION = .html
162HTML_HEADER =
163HTML_FOOTER =
164HTML_STYLESHEET =
165HTML_ALIGN_MEMBERS = YES
166GENERATE_HTMLHELP = NO
167CHM_FILE =
168HHC_LOCATION =
169GENERATE_CHI = NO
170BINARY_TOC = NO
171TOC_EXPAND = NO
172DISABLE_INDEX = NO
173ENUM_VALUES_PER_LINE = 4
174GENERATE_TREEVIEW = NO
175TREEVIEW_WIDTH = 250
176#---------------------------------------------------------------------------
177# configuration options related to the LaTeX output
178#---------------------------------------------------------------------------
179GENERATE_LATEX = NO
180LATEX_OUTPUT = latex
181LATEX_CMD_NAME = latex
182MAKEINDEX_CMD_NAME = makeindex
183COMPACT_LATEX = NO
184PAPER_TYPE = letter
185EXTRA_PACKAGES =
186LATEX_HEADER =
187PDF_HYPERLINKS = YES
188USE_PDFLATEX = YES
189LATEX_BATCHMODE = NO
190LATEX_HIDE_INDICES = NO
191#---------------------------------------------------------------------------
192# configuration options related to the RTF output
193#---------------------------------------------------------------------------
194GENERATE_RTF = NO
195RTF_OUTPUT = rtf
196COMPACT_RTF = NO
197RTF_HYPERLINKS = YES
198RTF_STYLESHEET_FILE =
199RTF_EXTENSIONS_FILE =
200#---------------------------------------------------------------------------
201# configuration options related to the man page output
202#---------------------------------------------------------------------------
203GENERATE_MAN = NO
204MAN_OUTPUT = man
205MAN_EXTENSION = .3
206MAN_LINKS = NO
207#---------------------------------------------------------------------------
208# configuration options related to the XML output
209#---------------------------------------------------------------------------
210GENERATE_XML = NO
211XML_OUTPUT = xml
212XML_SCHEMA =
213XML_DTD =
214XML_PROGRAMLISTING = YES
215#---------------------------------------------------------------------------
216# configuration options for the AutoGen Definitions output
217#---------------------------------------------------------------------------
218GENERATE_AUTOGEN_DEF = NO
219#---------------------------------------------------------------------------
220# configuration options related to the Perl module output
221#---------------------------------------------------------------------------
222GENERATE_PERLMOD = NO
223PERLMOD_LATEX = YES
224PERLMOD_PRETTY = YES
225PERLMOD_MAKEVAR_PREFIX =
226#---------------------------------------------------------------------------
227# Configuration options related to the preprocessor
228#---------------------------------------------------------------------------
229ENABLE_PREPROCESSING = YES
230MACRO_EXPANSION = YES
231EXPAND_ONLY_PREDEF = NO
232SEARCH_INCLUDES = YES
233INCLUDE_PATH =
234INCLUDE_FILE_PATTERNS =
235PREDEFINED =
236EXPAND_AS_DEFINED =
237SKIP_FUNCTION_MACROS = YES
238#---------------------------------------------------------------------------
239# Configuration::additions related to external references
240#---------------------------------------------------------------------------
241TAGFILES =
242GENERATE_TAGFILE =
243ALLEXTERNALS = NO
244EXTERNAL_GROUPS = YES
245PERL_PATH = /usr/bin/perl
246#---------------------------------------------------------------------------
247# Configuration options related to the dot tool
248#---------------------------------------------------------------------------
249CLASS_DIAGRAMS = YES
250HIDE_UNDOC_RELATIONS = YES
251HAVE_DOT = YES
252CLASS_GRAPH = YES
253COLLABORATION_GRAPH = YES
254GROUP_GRAPHS = YES
255UML_LOOK = NO
256TEMPLATE_RELATIONS = NO
257INCLUDE_GRAPH = YES
258INCLUDED_BY_GRAPH = YES
259CALL_GRAPH = NO
260GRAPHICAL_HIERARCHY = YES
261DIRECTORY_GRAPH = NO
262DOT_IMAGE_FORMAT = png
263DOT_PATH =
264DOTFILE_DIRS =
265MAX_DOT_GRAPH_WIDTH = 1024
266MAX_DOT_GRAPH_HEIGHT = 1024
267MAX_DOT_GRAPH_DEPTH = 1000
268DOT_TRANSPARENT = NO
269DOT_MULTI_TARGETS = NO
270GENERATE_LEGEND = YES
271DOT_CLEANUP = YES
272#---------------------------------------------------------------------------
273# Configuration::additions related to the search engine
274#---------------------------------------------------------------------------
275SEARCHENGINE = NO
diff --git a/build.conf b/build.conf
new file mode 100644
index 0000000..cab11a8
--- /dev/null
+++ b/build.conf
@@ -0,0 +1,35 @@
1# build.conf for build, kind of whacky, eh?
2
3default action: check targets()
4
5set "CXXFLAGS" += "-ggdb -W -Wall"
6
7"build":
8 rule "exe",
9 target file,
10 requires "libbu++/libbu++.a",
11 set "LDFLAGS" += "-Llibbu++ -lbu++ -ldl",
12 set "CXXFLAGS" += "-Ilibbu++",
13 input filesIn("src") filter regexp(".*\\.(cpp|y|l)$")
14
15rule "exe":
16 matches regexp("(.*)\\.o$"),
17 aggregate toString(" "),
18 perform command("g++ -o {target} {match} {LDFLAGS}")
19
20rule "cpp":
21 matches regexp("(.*)\\.(cpp|c)$"),
22 produces "{re:1}.o",
23 requires commandToList("g++ -M {CXXFLAGS} {match}", "make"),
24 perform command("g++ {CXXFLAGS} -c -o {target} {match}")
25
26rule "bison":
27 matches regexp("(.*)\\.y$"),
28 produces ["{re:1}.tab.c", "{re:1}.tab.h", "{re:1}.output"],
29 perform command("bison -v -b{re:1} {match}")
30
31rule "flex":
32 matches regexp("(.*)\\.l$"),
33 produces ["{re:1}.yy.c","{re:1}.yy.h"],
34 perform command("flex {FLEXFLAGS} {match}")
35
diff --git a/default.bld b/default.bld
new file mode 100644
index 0000000..9b71c4b
--- /dev/null
+++ b/default.bld
@@ -0,0 +1,129 @@
1#
2# default.bld for build mark 3. This guy does it all, so watch out!
3# :)
4#
5
6CXXFLAGS += "-ggdb -W -Wall";
7
8action "default"
9{
10 build: "build-m3";
11}
12
13action "man"
14{
15 build: targets("all");
16}
17
18action "pkg"
19{
20 build: targets("pkg");
21}
22
23action "clean"
24{
25 clean: "build-m3";
26}
27
28action "devinstall"
29{
30 if "$(id -u)" != "0\n" then
31 {
32 error "You can only install as the root user, su then run this.";
33 }
34 install: targets("devinstall");
35}
36
37action "viminstall"
38{
39 if "$(id -u)" != "0\n" then
40 {
41 error "You can only install as the root user, su then run this.";
42 }
43 install: targets("viminstall");
44}
45
46target "build-m3"
47{
48 input [files("src/*.y"), files("src/*.l"), files("src/*.cpp")];
49 rule "exe";
50 LDFLAGS += "-Llibbu++ -lbu++ -ldl";
51 CXXFLAGS += "-Ilibbu++";
52 tag "tools";
53}
54
55target "build-r$(svnversion "-n").tar.bz2"
56{
57 input [
58 "build.conf",
59 "Doxyfile",
60 "default.bld",
61 "docs/build.1",
62 "docs/build.7",
63 "support/vim/syntax/build.vim",
64 "support/vim/ftdetect/build.vim",
65 "support/vim/ftplugin/build.vim",
66 files("src/*.y"),
67 files("src/*.l"),
68 files("src/*.cpp"),
69 files("src/*.h"),
70 files("share/autoinclude/*"),
71 files("share/include/*")
72 ];
73 rule "tarball";
74 tag "pkg";
75}
76
77// We want to override the c rule, and just use c++ for it
78rule "c"
79{
80 input "*.c";
81 output replace(".c", ".o");
82 tag "auto-source";
83 requires getMakeDeps("g++ ${CXXFLAGS} -M ${INPUT}");
84 profile "build"
85 {
86 execute("g++ ${CXXFLAGS} -c -o ${OUTPUT} ${INPUT}");
87 }
88}
89
90rule "tarball"
91{
92 input matches("*.cpp", "*.h", "*.c", "*.y", "*.l", "*.conf", "Doxyfile",
93 "*.1", "*.7", "*.vim");
94 profile "build"
95 {
96 OUTDIR = OUTPUT.replace(".tar.bz2","");
97 execute("tar --transform=\"s@^@${OUTDIR}/@\" -c ${INPUT} |"
98 " bzip2 -9 > ${OUTPUT}");
99 }
100}
101
102target "/usr/share/build"
103{
104 input "$(echo -n $PWD)/share";
105 tag "devinstall";
106 profile "install"
107 {
108 execute("ln -fsv ${INPUT} \"${OUTPUT}\"");
109 }
110}
111
112for vimdir in dirs("/usr/share/vim/vim*") do
113{
114 for ftype in ["ftplugin/build.vim", "ftdetect/build.vim",
115 "syntax/build.vim"] do
116 {
117 target vimdir + "/" + ftype
118 {
119 input "support/vim/" + ftype;
120 tag "viminstall";
121 display "install";
122 profile "install"
123 {
124 execute("cp -f ${INPUT} ${OUTPUT}");
125 }
126 }
127 }
128}
129
diff --git a/docs/build.1 b/docs/build.1
new file mode 100644
index 0000000..118c744
--- /dev/null
+++ b/docs/build.1
@@ -0,0 +1,5 @@
1.TH build 1 "October 30, 2009" "Xagasoft" "Geekgene"
2Build is awesome, try it out
3.P
4This is a new paragraph
5.SH SYNOPSIS
diff --git a/docs/build.7 b/docs/build.7
new file mode 100644
index 0000000..5d6ab34
--- /dev/null
+++ b/docs/build.7
@@ -0,0 +1,84 @@
1.TH build 1 "October 30, 2009" "Xagasoft" "Geekgene"
2.SH NAME
3.P
4build.conf configuration files.
5.SH SYNOPSIS
6.P
7These files describe how to build something, generally software from source
8code, but it can really be anything.
9.SH DESCRIPTION
10.P
11For now, I'm just going to list some things here, here are the functions
12available to you for general use at the moment:
13.TP
14.B
15dirs()
16Takes any number of string parameters, matches the file system based on glob
17patterns and returns all directories that match the given patterns.
18.TP
19.B
20files()
21Takes any number of string parameters, matches the file system based on glob
22patterns and returns all files that match the given patterns.
23.TP
24.B
25execute()
26Takes one string parameter, and executes it as a command using your shell.
27.TP
28.B
29exists()
30Checks to see if the given file/directory/etc exists on the file system, if it
31does, true is returned, otherwise, false.
32.TP
33.B
34matches()
35Checks the input of the function to see if it matches a given wildcard pattern,
36the pattern may include any number of * (meaning match any number of any
37character) or ? (meaning match one of any character) symbols. If the input is
38a string, then true or false is returned. If the input is a list, a filtered
39version of the list is returned, containing only the elements that matched the
40pattern.
41.TP
42.B
43replace()
44Takes two string parameters and replaces all occurances of the first with the
45second in the input string or list of strings.
46.TP
47.B
48targets()
49Returns a list of target outputs, this will return every output for any target
50that it finds seperately, so targets with multiple outputs will be in the list
51more than once. With no parametrs it returns a list of all explicitly defined
52targets, with a string parameter it returns all targets that have the given
53tag.
54.TP
55.B
56toString()
57Takes the input and converts it to a string, with no parameters it uses default
58formatting, this means that for lists it puts a space between each element.
59.TP
60.B
61unlink()
62Unlinks (deletes) the given file or files from the filesystem. It can take any
63number of string or string list parameters.
64.TP
65.B
66getMakeDeps()
67Returns a list of files ready for use in a requires statement from the stdout of the command provided in the only string parameter. The command should output the dependancies in makefile format.
68.TP
69.B
70fileName()
71Returns the file portion of the string provided as input. That is, it returns everything after the last '/' character. This may not always be a file, it could be a directory, or even just an arbitrary part of a string that may or may not have '/' in it. If the input ends with a slash this will return an empty string.
72.TP
73.B
74dirName()
75Returns the directory portion of the string provided as input. That is, it returns everything before the last '/' character. Read the caveat for fileName about what this really means. If the input ends with a slash this will return the entire string, except the trailing slash.
76.P
77Here, lets list functions we wish we had...
78.SH ENVIRONMENT
79.SH FILES
80.SH CONFORMING TO
81.SH NOTES
82.SH BUGS
83.SH EXAMPLE
84.SH SEE ALSO
diff --git a/docs/buildm3notes.txt b/docs/buildm3notes.txt
new file mode 100644
index 0000000..72e68e4
--- /dev/null
+++ b/docs/buildm3notes.txt
@@ -0,0 +1,99 @@
1Rules
2Targets
3 Profiles
4 Build
5 Clean
6 Install
7 Package
8 etc.
9 Dependancies
10 Force Ruleset / mode
11 Input
12 Produces / Output
13Actions (functions)
14 Execute
15 Delete
16 Download
17 GetDependencies
18 ExecuteTest
19 RunTarget
20
21Mode Variables - switch modes for a variety of things (debug/release, platforms)
22
23Includes (including default includes)
24
25Bash style variable replacements / execute / etc.
26
27Functions?
28 files() returns list (globbing)
29 querySystemParam()
30
31if blocks can be anywhere.
32
33semi-typed variables, default to string
34
35variables in a target: INPUT, OUTPUT (by default instead of target and match)
36
37---- ast processing order ----
381) import all environment variables
392) import all cached data
403) set all variables specified on the command line
414) root level "script"
424.1) execute root level items, for target, rule, and non-auto, non-global config
43 this means building the target, rule, and config tables.
44 for global config and auto-config, run them as we hit them.
455) execute specified target(s) with specified profile
46
47---- pseudo code -----
48
49set CXXFLAGS += "param"; // ensure there are spaces so it works for a command
50set CXXFLAGS << "param"; // append like you'd expect, no extra changes
51
52unset PREFIX; // may be handy to be able to unset things
53
54include "otherbuild.h"; // oh yeah includes
55
56notice "something good?"; // display a notice to the user, just info...
57warning "yeah yeah yeah..."; // display a warning message, but don't exit
58error "something or other"; // exit immediately with an error message
59
60target "libbu++.a"
61{
62 input files("src/*.cpp");
63 set CXXFLAGS="-ggdb";
64 condition file;
65 requires [];
66
67 hidden; // Hides this from the user, but still callable
68}
69
70target ["file a", files("src/*.cpp), files("src/*y")].replace("bob", "sam")...
71{
72 condition always;
73}
74
75auto config "X"
76{
77}
78
79config "EXTRA_DEBUG"
80{
81 display "Debugging/Extra debugging";
82 type int; (or string, or bool, or list, or version)
83 default 0;
84 allow ["0-5","6"];
85
86}
87
88rule "cpp"
89{
90 input glob("*.cpp"); // or just a string, pretend it's a glob
91 output "${INPUT}".replace(".cpp",".o");
92 requires getDeps("g++ ${CXXFLAGS} -M ${OUTPUT} ${INPUT}", "make");
93 profile "build"
94 {
95 fnc1() tag "c++";
96 fnc2();
97 }
98}
99
diff --git a/share/autoinclude/general-rules.bld b/share/autoinclude/general-rules.bld
new file mode 100644
index 0000000..16217e0
--- /dev/null
+++ b/share/autoinclude/general-rules.bld
@@ -0,0 +1,64 @@
1
2rule "exe"
3{
4 input "*.o";
5 profile "build"
6 {
7 execute("g++ -o ${OUTPUT} ${INPUT} ${LDFLAGS}");
8 }
9}
10
11rule "lib"
12{
13 input "*.o";
14 profile "build"
15 {
16 execute("ar cr ${OUTPUT} ${INPUT}");
17 }
18}
19
20rule "cpp"
21{
22 input "*.cpp";
23 output replace(".cpp", ".o");
24 requires getMakeDeps("g++ ${CXXFLAGS} -M ${INPUT}");
25 profile "build"
26 {
27 execute("g++ ${CXXFLAGS} -c -o ${OUTPUT} ${INPUT}", "g++");
28 }
29}
30
31// Heh, we're not going to use this one.
32rule "c"
33{
34 input "*.c";
35 output replace(".c", ".o");
36 requires getMakeDeps("gcc ${CXXFLAGS} -M ${INPUT}");
37 profile "build"
38 {
39 execute("gcc ${CFLAGS} -c -o ${OUTPUT} ${INPUT}");
40 }
41}
42
43rule "bison"
44{
45 input "*.y";
46 output [INPUT.replace(".y", ".tab.c"), INPUT.replace(".y", ".tab.h")];
47 profile "build"
48 {
49 BASE = INPUT.replace(".y", "");
50 execute("bison -b${BASE} ${INPUT}");
51 // if you add a -v bison will produce a .output file
52 }
53}
54
55rule "flex"
56{
57 input "*.l";
58 output replace(".l", ".yy.c");
59 output replace(".l", ".yy.h");
60 profile "build"
61 {
62 execute("flex ${FLEXFLAGS} ${INPUT}");
63 }
64}
diff --git a/share/include/qt4.bld b/share/include/qt4.bld
new file mode 100644
index 0000000..749cc52
--- /dev/null
+++ b/share/include/qt4.bld
@@ -0,0 +1,96 @@
1
2function qt_uiToH()
3{
4 if "${UI_DIR}" == "" then
5 {
6 DIR = INPUT.dirName();
7 }
8 else
9 {
10 DIR = UI_DIR;
11 }
12 FILE = INPUT.fileName();
13 OUTFILE = FILE.replace(".ui",".h");
14 return "${DIR}/ui_${OUTFILE}";
15}
16
17rule "qt_ui"
18{
19 display "ui";
20 input "*.ui";
21 output INPUT.qt_uiToH();
22 tag "headers";
23 profile "build"
24 {
25 execute("${QTDIR}/bin/uic -o ${OUTPUT} ${INPUT}");
26 }
27}
28
29function qt_qrcToCpp()
30{
31 if "${RCC_DIR}" == "" then
32 {
33 DIR = INPUT.dirName();
34 }
35 else
36 {
37 DIR = RCC_DIR;
38 }
39 FILE = INPUT.fileName();
40 OUTFILE = FILE.replace(".qrc",".cpp");
41 return "${DIR}/qrc_${OUTFILE}";
42}
43
44rule "qt_rcc"
45{
46 display "rcc";
47 input "*.qrc";
48 output INPUT.qt_qrcToCpp();
49 profile "build"
50 {
51 NAME = INPUT.fileName().replace(".qrc","");
52 execute("${QTDIR}/bin/rcc -name ${NAME} -o ${OUTPUT} ${INPUT}");
53 }
54}
55
56function qt_isMocable()
57{
58 if INPUT.matches("*.h") then
59 {
60 if exists( INPUT ) then
61 {
62 if "$(grep Q_OBJECT ${INPUT})" != "" then
63 {
64 return true;
65 }
66 }
67 }
68 return false;
69}
70
71function qt_hToMocCpp()
72{
73 if "${MOC_DIR}" == "" then
74 {
75 DIR = INPUT.dirName();
76 }
77 else
78 {
79 DIR = MOC_DIR;
80 }
81 FILE = INPUT.fileName();
82 OUTFILE = FILE.replace(".h",".cpp");
83 return "${DIR}/moc_${OUTFILE}";
84}
85
86rule "qt_moc"
87{
88 display "moc";
89 input qt_isMocable();
90 output INPUT.qt_hToMocCpp();
91 profile "build"
92 {
93 execute("${QTDIR}/bin/moc -o${OUTPUT} ${INPUT}");
94 }
95}
96
diff --git a/src/action.cpp b/src/action.cpp
new file mode 100644
index 0000000..23c24cd
--- /dev/null
+++ b/src/action.cpp
@@ -0,0 +1,107 @@
1#include "action.h"
2#include "ast.h"
3#include "astbranch.h"
4#include "astleaf.h"
5#include "runner.h"
6#include "variable.h"
7
8Action::Action( const class AstBranch *pRoot ) :
9 pRoot( pRoot ),
10 pAst( NULL )
11{
12 sName = dynamic_cast<AstLeaf *>(
13 *(*pRoot->getBranchBegin()).begin()
14 )->getStrValue();
15}
16
17Action::~Action()
18{
19 delete pAst;
20 pAst = NULL;
21}
22
23const Bu::FString &Action::getName() const
24{
25 return sName;
26}
27
28void Action::call( class Runner *pRunner )
29{
30 pRunner->run( (*(pRoot->getBranchBegin()+1)).begin() );
31}
32
33Action *Action::genDefaultAll()
34{
35 Ast *pAst = new Ast();
36 pAst->addNode( AstNode::typeActionDef );
37 pAst->openBranch();
38 pAst->addNode( AstNode::typeString, "all" );
39 pAst->openBranch();
40 pAst->addNode( AstNode::typeProcessTarget );
41 pAst->openBranch();
42 pAst->addNode( AstNode::typeString, "build" );
43 pAst->openBranch();
44 pAst->addNode( AstNode::typeFunction );
45 pAst->openBranch();
46 pAst->addNode( AstNode::typeString, "targets" );
47 pAst->closeNode();
48 pAst->closeNode();
49 pAst->closeNode();
50 Action *pRet = new Action(
51 dynamic_cast<const AstBranch *>( *pAst->getNodeBegin() )
52 );
53 pRet->pAst = pAst;
54
55 return pRet;
56}
57
58Action *Action::genDefaultClean()
59{
60 Ast *pAst = new Ast();
61 pAst->addNode( AstNode::typeActionDef );
62 pAst->openBranch();
63 pAst->addNode( AstNode::typeString, "clean" );
64 pAst->openBranch();
65 pAst->addNode( AstNode::typeProcessTarget );
66 pAst->openBranch();
67 pAst->addNode( AstNode::typeString, "clean" );
68 pAst->openBranch();
69 pAst->addNode( AstNode::typeFunction );
70 pAst->openBranch();
71 pAst->addNode( AstNode::typeString, "targets" );
72 pAst->closeNode();
73 pAst->closeNode();
74 pAst->closeNode();
75 Action *pRet = new Action(
76 dynamic_cast<const AstBranch *>( *pAst->getNodeBegin() )
77 );
78 pRet->pAst = pAst;
79
80 return pRet;
81}
82
83Action *Action::genDefaultDefault()
84{
85 Ast *pAst = new Ast();
86 pAst->addNode( AstNode::typeActionDef );
87 pAst->openBranch();
88 pAst->addNode( AstNode::typeString, "default" );
89 pAst->openBranch();
90 pAst->addNode( AstNode::typeProcessTarget );
91 pAst->openBranch();
92 pAst->addNode( AstNode::typeString, "build" );
93 pAst->openBranch();
94 pAst->addNode( AstNode::typeFunction );
95 pAst->openBranch();
96 pAst->addNode( AstNode::typeString, "targets" );
97 pAst->closeNode();
98 pAst->closeNode();
99 pAst->closeNode();
100 Action *pRet = new Action(
101 dynamic_cast<const AstBranch *>( *pAst->getNodeBegin() )
102 );
103 pRet->pAst = pAst;
104
105 return pRet;
106}
107
diff --git a/src/action.h b/src/action.h
new file mode 100644
index 0000000..520f2f1
--- /dev/null
+++ b/src/action.h
@@ -0,0 +1,26 @@
1#ifndef ACTION_H
2#define ACTION_H
3
4#include <bu/fstring.h>
5
6class Action
7{
8public:
9 Action( const class AstBranch *pRoot );
10 virtual ~Action();
11
12 const Bu::FString &getName() const;
13
14 void call( class Runner *pRunner );
15
16 static Action *genDefaultAll();
17 static Action *genDefaultClean();
18 static Action *genDefaultDefault();
19
20private:
21 Bu::FString sName;
22 const class AstBranch *pRoot;
23 class Ast *pAst;
24};
25
26#endif
diff --git a/src/ast.cpp b/src/ast.cpp
new file mode 100644
index 0000000..03ed4b6
--- /dev/null
+++ b/src/ast.cpp
@@ -0,0 +1,98 @@
1#include "ast.h"
2
3#include "astleaf.h"
4#include "astbranch.h"
5
6Ast::Ast()
7{
8}
9
10Ast::~Ast()
11{
12}
13
14void Ast::addNode( AstNode::Type eType )
15{
16 switch( eType&AstNode::typeClassMask )
17 {
18 case AstNode::typeBranch:
19 {
20 AstBranch *pNode = new AstBranch( eType );
21 addNode( pNode );
22 sBranch.push( pNode );
23 }
24 break;
25
26 case AstNode::typeLeaf:
27 {
28 AstLeaf *pNode = new AstLeaf( eType );
29 addNode( pNode );
30 }
31 break;
32
33 default:
34 throw Bu::ExceptionBase("You got it wrong.");
35 break;
36 }
37}
38
39void Ast::addNode( AstNode::Type eType, int iVal )
40{
41 addNode( new AstLeaf( eType, iVal ) );
42}
43
44void Ast::addNode( AstNode::Type eType, float fVal )
45{
46 addNode( new AstLeaf( eType, fVal ) );
47}
48
49void Ast::addNode( AstNode::Type eType, bool bVal )
50{
51 addNode( new AstLeaf( eType, bVal ) );
52}
53
54void Ast::addNode( AstNode::Type eType, const Bu::FString &sVal )
55{
56 addNode( new AstLeaf( eType, sVal ) );
57}
58
59void Ast::addNode( AstNode::Type eType, const char *sVal )
60{
61 addNode( new AstLeaf( eType, sVal ) );
62}
63
64void Ast::addNode( AstNode *pNode )
65{
66 if( sBranch.isEmpty() )
67 lNode.append( pNode );
68 else
69 sBranch.peek()->addNode( pNode );
70}
71
72void Ast::openBranch()
73{
74 sBranch.peek()->addBranch();
75}
76
77void Ast::closeNode()
78{
79 sBranch.pop();
80}
81
82Ast::NodeList::const_iterator Ast::getNodeBegin() const
83{
84 return lNode.begin();
85}
86
87Bu::Formatter &operator<<( Bu::Formatter &f, const Ast &a )
88{
89 f << "Abstract Syntax Tree:";
90 f.incIndent();
91 f << f.nl;
92 for( Ast::NodeList::const_iterator i = a.getNodeBegin(); i; i++ )
93 f << **i << f.nl;
94 f << f.nl;
95 f.decIndent();
96 return f;
97}
98
diff --git a/src/ast.h b/src/ast.h
new file mode 100644
index 0000000..a4f9361
--- /dev/null
+++ b/src/ast.h
@@ -0,0 +1,44 @@
1#ifndef AST_H
2#define AST_H
3
4#include "bu/list.h"
5#include "bu/stack.h"
6#include "bu/fstring.h"
7#include "bu/formatter.h"
8
9#include "astnode.h"
10
11/**
12 * Abstract Symbol Tree. This is the thing that the parser builds for us. In
13 * the end, this is also what we "run" when we run build files.
14 */
15class Ast
16{
17public:
18 typedef Bu::List<AstNode *> NodeList;
19 Ast();
20 virtual ~Ast();
21
22 void addNode( AstNode::Type eType );
23 void addNode( AstNode::Type eType, int iVal );
24 void addNode( AstNode::Type eType, float fVal );
25 void addNode( AstNode::Type eType, bool bVal );
26 void addNode( AstNode::Type eType, const Bu::FString &sVal );
27 void addNode( AstNode::Type eType, const char *sVal );
28 void addNode( AstNode *pNode );
29
30 void openBranch();
31
32 void closeNode();
33
34 NodeList::const_iterator getNodeBegin() const;
35
36private:
37 NodeList lNode;
38 typedef Bu::Stack<class AstBranch *> BranchStack;
39 BranchStack sBranch;
40};
41
42Bu::Formatter &operator<<( Bu::Formatter &f, const Ast &a );
43
44#endif
diff --git a/src/astbranch.cpp b/src/astbranch.cpp
new file mode 100644
index 0000000..a6aa21c
--- /dev/null
+++ b/src/astbranch.cpp
@@ -0,0 +1,44 @@
1#include "astbranch.h"
2
3AstBranch::AstBranch( Type eType ) :
4 AstNode( eType )
5{
6}
7
8AstBranch::~AstBranch()
9{
10}
11
12void AstBranch::addBranch()
13{
14 lBranch.append( NodeList() );
15}
16
17void AstBranch::addNode( AstNode *pNode )
18{
19 lBranch.last().append( pNode );
20}
21
22AstBranch::BranchList::const_iterator AstBranch::getBranchBegin() const
23{
24 return lBranch.begin();
25}
26
27Bu::Formatter &operator<<( Bu::Formatter &f, const AstBranch &l )
28{
29 f.incIndent();
30 f << ":";
31 for( AstBranch::BranchList::const_iterator i = l.getBranchBegin(); i; i++ )
32 {
33 f << f.nl << "Branch:";
34 f.incIndent();
35 for( AstBranch::NodeList::const_iterator j = i->begin(); j; j++ )
36 {
37 f << f.nl << **j;
38 }
39 f.decIndent();
40 }
41 f.decIndent();
42 return f;
43}
44
diff --git a/src/astbranch.h b/src/astbranch.h
new file mode 100644
index 0000000..5ff8606
--- /dev/null
+++ b/src/astbranch.h
@@ -0,0 +1,27 @@
1#ifndef AST_BRANCH_H
2#define AST_BRANCH_H
3
4#include "bu/list.h"
5#include "astnode.h"
6#include "bu/formatter.h"
7
8class AstBranch : public AstNode
9{
10public:
11 typedef Bu::List<AstNode *> NodeList;
12 typedef Bu::List<NodeList> BranchList;
13 AstBranch( Type eType );
14 virtual ~AstBranch();
15
16 void addBranch();
17 void addNode( AstNode *pNode );
18
19 BranchList::const_iterator getBranchBegin() const;
20
21private:
22 BranchList lBranch;
23};
24
25Bu::Formatter &operator<<( Bu::Formatter &f, const AstBranch &l );
26
27#endif
diff --git a/src/astleaf.cpp b/src/astleaf.cpp
new file mode 100644
index 0000000..62773ce
--- /dev/null
+++ b/src/astleaf.cpp
@@ -0,0 +1,137 @@
1#include "astleaf.h"
2
3AstLeaf::AstLeaf( Type eType ) :
4 AstNode( eType ),
5 sVal( NULL )
6{
7}
8
9AstLeaf::AstLeaf( Type eType, int iNew ) :
10 AstNode( eType ),
11 sVal( NULL )
12{
13 setIntValue( iNew );
14}
15
16AstLeaf::AstLeaf( Type eType, float fNew ) :
17 AstNode( eType ),
18 sVal( NULL )
19{
20 setFloatValue( fNew );
21}
22
23AstLeaf::AstLeaf( Type eType, bool bNew ) :
24 AstNode( eType ),
25 sVal( NULL )
26{
27 setBoolValue( bNew );
28}
29
30AstLeaf::AstLeaf( Type eType, const Bu::FString &sNew ) :
31 AstNode( eType ),
32 sVal( NULL )
33{
34 setStrValue( sNew );
35}
36
37AstLeaf::AstLeaf( Type eType, const char *sNew ) :
38 AstNode( eType ),
39 sVal( NULL )
40{
41 setStrValue( sNew );
42}
43
44AstLeaf::~AstLeaf()
45{
46 if( getDataType() == typeDataString )
47 delete sVal;
48}
49
50void AstLeaf::setIntValue( int iNew )
51{
52 if( getDataType() != typeDataInt )
53 throw Bu::ExceptionBase("Type is not int.");
54 iVal = iNew;
55}
56
57void AstLeaf::setFloatValue( float fNew )
58{
59 if( getDataType() != typeDataFloat )
60 throw Bu::ExceptionBase("Type is not float.");
61 fVal = fNew;
62}
63
64void AstLeaf::setBoolValue( bool bNew )
65{
66 if( getDataType() != typeDataBool )
67 throw Bu::ExceptionBase("Type is not bool.");
68 bVal = bNew;
69}
70
71void AstLeaf::setStrValue( const Bu::FString &sNew )
72{
73 if( getDataType() != typeDataString )
74 throw Bu::ExceptionBase("Type is not string.");
75 if( sVal == NULL )
76 sVal = new Bu::FString( sNew );
77 else
78 *sVal = sNew;
79}
80
81int AstLeaf::getIntValue() const
82{
83 if( getDataType() != typeDataInt )
84 throw Bu::ExceptionBase("Type is not int.");
85 return iVal;
86}
87
88float AstLeaf::getFloatValue() const
89{
90 if( getDataType() != typeDataFloat )
91 throw Bu::ExceptionBase("Type is not float.");
92 return fVal;
93}
94
95bool AstLeaf::getBoolValue() const
96{
97 if( getDataType() != typeDataBool )
98 throw Bu::ExceptionBase("Type is not bool.");
99 return bVal;
100}
101
102Bu::FString &AstLeaf::getStrValue() const
103{
104 if( getDataType() != typeDataString )
105 throw Bu::ExceptionBase("Type is not string.");
106 return *sVal;
107}
108
109Bu::Formatter &operator<<( Bu::Formatter &f, const AstLeaf &l )
110{
111 switch( l.getDataType() )
112 {
113 case AstNode::typeDataInt:
114 f << ": " << l.getIntValue();
115 break;
116
117 case AstNode::typeDataFloat:
118 f << ": " << l.getFloatValue();
119 break;
120
121 case AstNode::typeDataBool:
122 f << ": " << l.getBoolValue();
123 break;
124
125 case AstNode::typeDataString:
126 f << ": '" << l.getStrValue() << "'";
127 break;
128
129 case AstNode::typeDataNone:
130 break;
131
132 default:
133 f << ": " << "!! Invalid Type !!";
134 }
135 return f;
136}
137
diff --git a/src/astleaf.h b/src/astleaf.h
new file mode 100644
index 0000000..0c8911b
--- /dev/null
+++ b/src/astleaf.h
@@ -0,0 +1,41 @@
1#ifndef AST_LEAF_H
2#define AST_LEAF_H
3
4#include "astnode.h"
5#include "bu/fstring.h"
6#include "bu/formatter.h"
7
8class AstLeaf : public AstNode
9{
10public:
11 AstLeaf( Type eType );
12 AstLeaf( Type eType, int iNew );
13 AstLeaf( Type eType, float fNew );
14 AstLeaf( Type eType, bool bNew );
15 AstLeaf( Type eType, const Bu::FString &sNew );
16 AstLeaf( Type eType, const char *sNew );
17 virtual ~AstLeaf();
18
19 void setIntValue( int iNew );
20 void setFloatValue( float fNew );
21 void setBoolValue( bool bNew );
22 void setStrValue( const Bu::FString &sNew );
23
24 int getIntValue() const;
25 float getFloatValue() const;
26 bool getBoolValue() const;
27 Bu::FString &getStrValue() const;
28
29private:
30 union
31 {
32 int iVal;
33 float fVal;
34 bool bVal;
35 Bu::FString *sVal;
36 };
37};
38
39Bu::Formatter &operator<<( Bu::Formatter &f, const AstLeaf &l );
40
41#endif
diff --git a/src/astnode.cpp b/src/astnode.cpp
new file mode 100644
index 0000000..5adb3ee
--- /dev/null
+++ b/src/astnode.cpp
@@ -0,0 +1,113 @@
1#include "astnode.h"
2#include "astleaf.h"
3#include "astbranch.h"
4
5AstNode::AstNode( Type eType ) :
6 eType( eType )
7{
8}
9
10AstNode::~AstNode()
11{
12}
13
14Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode &n )
15{
16 f << n.getType();
17 if( n.getClass() == AstNode::typeBranch )
18 {
19 f << *dynamic_cast<const AstBranch *>(&n);
20 }
21 else
22 {
23 f << *dynamic_cast<const AstLeaf *>(&n);
24 }
25 return f;
26}
27
28Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode::Type &t )
29{
30 switch( t )
31 {
32 case AstNode::typeFunction: f << "Function"; break;
33 case AstNode::typeSet: f << "Set"; break;
34 case AstNode::typeUnset: f << "Unset"; break;
35 case AstNode::typeIf: f << "If"; break;
36 case AstNode::typeInclude: f << "Include"; break;
37 case AstNode::typeTarget: f << "Target"; break;
38 case AstNode::typeRule: f << "Rule"; break;
39 case AstNode::typeConfig: f << "Config"; break;
40 case AstNode::typeList: f << "List"; break;
41 case AstNode::typeInlineFunc: f << "InlineFunc"; break;
42 case AstNode::typeRequires: f << "Requires"; break;
43 case AstNode::typeFor: f << "For"; break;
44 case AstNode::typeFunctionDef: f << "FunctionDef"; break;
45 case AstNode::typeReturn: f << "Return"; break;
46 case AstNode::typeProfile: f << "Profile"; break;
47 case AstNode::typeInput: f << "Input"; break;
48 case AstNode::typeRuleDef: f << "RuleDef"; break;
49 case AstNode::typeOutput: f << "Output"; break;
50 case AstNode::typeAutoConfig: f << "AutoConfig"; break;
51 case AstNode::typeGlobalConfig: f << "GlobalConfig"; break;
52 case AstNode::typeType: f << "Type"; break;
53 case AstNode::typeValue: f << "Value"; break;
54 case AstNode::typeAllow: f << "Allow"; break;
55 case AstNode::typeDefault: f << "Default"; break;
56 case AstNode::typeExport: f << "Export"; break;
57 case AstNode::typeExpr: f << "Expr"; break;
58 case AstNode::typeActionDef: f << "ActionDef"; break;
59 case AstNode::typeProcessTarget:f << "ProcessTarget"; break;
60 case AstNode::typeTag: f << "Tag"; break;
61
62 case AstNode::typeVariable: f << "Variable"; break;
63 case AstNode::typeString: f << "String"; break;
64 case AstNode::typeInt: f << "Int"; break;
65 case AstNode::typeFloat: f << "Float"; break;
66 case AstNode::typeBool: f << "Bool"; break;
67 case AstNode::typeVersion: f << "Version"; break;
68 case AstNode::typeOpEq: f << "Operator ="; break;
69 case AstNode::typeOpPlusEq: f << "Operator +="; break;
70 case AstNode::typeOpPlusEqRaw: f << "Operator <<"; break;
71 case AstNode::typeError: f << "Error"; break;
72 case AstNode::typeWarning: f << "Warning"; break;
73 case AstNode::typeNotice: f << "Notice"; break;
74 case AstNode::typeTypeString: f << "Type String"; break;
75 case AstNode::typeTypeInt: f << "Type Int"; break;
76 case AstNode::typeTypeFloat: f << "Type Float"; break;
77 case AstNode::typeTypeBool: f << "Type Bool"; break;
78 case AstNode::typeTypeVersion: f << "Type Version"; break;
79 case AstNode::typeCmpEq: f << "Compare ="; break;
80 case AstNode::typeCmpLt: f << "Compare <"; break;
81 case AstNode::typeCmpGt: f << "Compare >"; break;
82 case AstNode::typeCmpNe: f << "Compare !="; break;
83 case AstNode::typeCmpLtEq: f << "Compare <="; break;
84 case AstNode::typeCmpGtEq: f << "Compare >="; break;
85 case AstNode::typeCondition: f << "Condition"; break;
86 case AstNode::typeDisplay: f << "Display"; break;
87 case AstNode::typeCache: f << "Cache"; break;
88 case AstNode::typePushPrefix: f << "Push Prefix"; break;
89 case AstNode::typePopPrefix: f << "Pop Prefix"; break;
90 case AstNode::typeNull: f << "Null"; break;
91 case AstNode::typeVariableRef: f << "VariableRef"; break;
92 case AstNode::typeOpPlus: f << "Operator +"; break;
93 case AstNode::typeOpMinus: f << "Operator -"; break;
94 case AstNode::typeOpMultiply: f << "Operator *"; break;
95 case AstNode::typeOpDivide: f << "Operator /"; break;
96 case AstNode::typeOpNegate: f << "Operator negate"; break;
97 case AstNode::typeOpNot: f << "Operator not"; break;
98
99 case AstNode::typeBranch: f << "Branch"; break;
100 case AstNode::typeLeaf: f << "Leaf"; break;
101 case AstNode::typeClassMask: f << "ClassMask"; break;
102
103 case AstNode::typeDataNone: f << "<Data None>"; break;
104 case AstNode::typeDataString: f << "<Data String>"; break;
105 case AstNode::typeDataInt: f << "<Data Int>"; break;
106 case AstNode::typeDataFloat: f << "<Data Float>"; break;
107 case AstNode::typeDataBool: f << "<Data Bool>"; break;
108 case AstNode::typeDataVersion: f << "<Data Version>"; break;
109 case AstNode::typeDataMask: f << "<Data Mask>"; break;
110 }
111 return f;
112}
113
diff --git a/src/astnode.h b/src/astnode.h
new file mode 100644
index 0000000..6ade25b
--- /dev/null
+++ b/src/astnode.h
@@ -0,0 +1,108 @@
1#ifndef AST_NODE_H
2#define AST_NODE_H
3
4#include "bu/formatter.h"
5
6class AstNode
7{
8public:
9 enum Type
10 {
11 // Branching types
12 typeFunction = 0x100001,
13 typeSet = 0x100002,
14 typeUnset = 0x100003,
15 typeIf = 0x100004,
16 typeInclude = 0x100005,
17 typeTarget = 0x100006,
18 typeRule = 0x100007,
19 typeConfig = 0x100008,
20 typeList = 0x100009,
21 typeInlineFunc = 0x10000A,
22 typeRequires = 0x10000B,
23 typeFor = 0x10000C,
24 typeFunctionDef = 0x10000D,
25 typeReturn = 0x10000E,
26 typeProfile = 0x10000F,
27 typeInput = 0x100010,
28 typeRuleDef = 0x100011,
29 typeOutput = 0x100012,
30 typeAutoConfig = 0x100013,
31 typeGlobalConfig = 0x100014,
32 typeType = 0x100015,
33 typeValue = 0x100016,
34 typeAllow = 0x100017,
35 typeDefault = 0x100018,
36 typeExport = 0x100019,
37 typeExpr = 0x10001A, /***< Stack based compound expression.*/
38 typeActionDef = 0x10001B,
39 typeProcessTarget = 0x10001C,
40 typeTag = 0x10001D,
41
42 // Leaf types
43 typeVariable = 0x210001,
44 typeString = 0x210002,
45 typeInt = 0x220003,
46 typeFloat = 0x230004,
47 typeBool = 0x240005,
48 typeVersion = 0x250006,
49 typeOpEq = 0x200007,
50 typeOpPlusEq = 0x200008,
51 typeOpPlusEqRaw = 0x200009,
52 typeError = 0x21000A,
53 typeWarning = 0x21000B,
54 typeNotice = 0x21000C,
55 typeTypeString = 0x20000D,
56 typeTypeInt = 0x20000E,
57 typeTypeFloat = 0x20000F,
58 typeTypeBool = 0x200010,
59 typeTypeVersion = 0x200011,
60 typeCmpEq = 0x200012,
61 typeCmpLt = 0x200013,
62 typeCmpGt = 0x200014,
63 typeCmpNe = 0x200015,
64 typeCmpLtEq = 0x200016,
65 typeCmpGtEq = 0x200017,
66 typeCondition = 0x210018,
67 typeDisplay = 0x210019,
68 typeCache = 0x24001A,
69 typePushPrefix = 0x21001B,
70 typePopPrefix = 0x20001C,
71 typeNull = 0x20001D,
72 typeVariableRef = 0x21001E,
73 typeOpPlus = 0x20001F,
74 typeOpMinus = 0x200020,
75 typeOpMultiply = 0x200021,
76 typeOpDivide = 0x200022,
77 typeOpNegate = 0x200023,
78 typeOpNot = 0x200024,
79
80 typeBranch = 0x100000,
81 typeLeaf = 0x200000,
82 typeClassMask = 0x300000,
83
84 typeDataNone = 0x000000,
85 typeDataString = 0x010000,
86 typeDataInt = 0x020000,
87 typeDataFloat = 0x030000,
88 typeDataBool = 0x040000,
89 typeDataVersion = 0x050000,
90
91 typeDataMask = 0x0F0000
92 };
93public:
94 AstNode( Type eType );
95 virtual ~AstNode();
96
97 Type getType() const { return eType; }
98 Type getClass() const { return (Type)(eType&typeClassMask); }
99 Type getDataType() const { return (Type)(eType&typeDataMask); }
100
101private:
102 Type eType;
103};
104
105Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode &n );
106Bu::Formatter &operator<<( Bu::Formatter &f, const AstNode::Type &t );
107
108#endif
diff --git a/src/build.l b/src/build.l
new file mode 100644
index 0000000..dc4ddda
--- /dev/null
+++ b/src/build.l
@@ -0,0 +1,216 @@
1%{
2 #include "buildparser.h"
3 #include "bu/fstring.h"
4
5char *fstrdup( const Bu::FString &s )
6{
7 char *sRet = new char[s.getSize()+1];
8 memcpy( sRet, s.getStr(), s.getSize()+1 );
9 return sRet;
10}
11
12char *rstrdup( const char *sIn )
13{
14 char *sRet = new char[strlen(sIn)+1];
15 strcpy( sRet, sIn );
16 return sRet;
17}
18void build_error( YYLTYPE *locp, yyscan_t yyscanner, BuildParser &bld, const char *msg );
19
20Bu::FString sBuf;
21int iStrDepth = 0;
22%}
23
24%{
25 #define YY_USER_ACTION yylloc->last_column += yyleng;
26%}
27
28%x strdq
29%x BlockComment
30%option noyywrap nounput batch debug bison-bridge bison-locations reentrant
31%option prefix="build_"
32%option outfile="src/build.yy.c"
33%option header-file="src/build.yy.h"
34%%
35\n+ {
36 yylloc->last_line += yyleng;
37 yylloc->first_line = yylloc->last_line;
38 yylloc->first_column = yylloc->last_column = 0;
39}
40[ \t\r]+ {
41 yylloc->first_line = yylloc->last_line;
42 yylloc->first_column = yylloc->last_column+1;
43}
44"#".* /* eol comment */
45"//".* /* eol comment */
46
47"/*" {
48 BEGIN( BlockComment );
49}
50
51<BlockComment>"*/" {
52 BEGIN( INITIAL );
53}
54
55<BlockComment>\n+ {
56 yylloc->last_column = -yyleng;
57 yylloc->last_line += yyleng;
58}
59<BlockComment>. { }
60 /*
61<BlockComment>[^*\n/]+ { }
62<BlockComment>"*"[^/\n]+ { }
63<BlockComment>"*"|"/" { }
64 */
65
66[(){}[\],.;=<>!+*/-] return yytext[0];
67"+=" return OP_ADDSETP;
68"<<" return OP_ADDSETR;
69"==" return OP_CMPEQUAL;
70"!=" return OP_INEQUAL;
71"<=" return OP_LTEQUAL;
72">=" return OP_GTEQUAL;
73"target" return TOK_TARGET;
74"input" return TOK_INPUT;
75"output" return TOK_OUTPUT;
76"unset" return TOK_UNSET;
77"set" return TOK_SET;
78"condition" return TOK_CONDITION;
79"requires" return TOK_REQUIRES;
80"auto" return TOK_AUTO;
81"config" return TOK_CONFIG;
82"display" return TOK_DISPLAY;
83"type" return TOK_TYPE;
84"int" return TOK_INT;
85"float" return TOK_FLOAT;
86"bool" return TOK_BOOL;
87"version" return TOK_VERSION;
88"string" return TOK_STRING;
89"default" return TOK_DEFAULT;
90"allow" return TOK_ALLOW;
91"rule" return TOK_RULE;
92"action" return TOK_ACTION;
93"profile" return TOK_PROFILE;
94"if" return TOK_IF;
95"then" return TOK_THEN;
96"else" return TOK_ELSE;
97"include" return TOK_INCLUDE;
98"warning" return TOK_WARNING;
99"error" return TOK_ERROR;
100"notice" return TOK_NOTICE;
101"cache" return TOK_CACHE;
102"always" return TOK_ALWAYS;
103"never" return TOK_NEVER;
104"global" return TOK_GLOBAL;
105"local" return TOK_LOCAL;
106"for" return TOK_FOR;
107"in" return TOK_IN;
108"do" return TOK_DO;
109"return" return TOK_RETURN;
110"function" return TOK_FUNCTION;
111"continue" return TOK_CONTINUE;
112"break" return TOK_BREAK;
113"value" return TOK_VALUE;
114"all" return TOK_ALL;
115"export" return TOK_EXPORT;
116"tag" return TOK_TAG;
117"null" return TOK_NULL;
118
119"true" {
120 yylval->bVal = true;
121 return BOOL;
122}
123"false" {
124 yylval->bVal = false;
125 return BOOL;
126}
127
128[a-zA-Z_][a-zA-Z0-9_]*: {
129 yytext[yyleng-1] = '\0';
130 yylval->sVal = rstrdup( yytext );
131 return PROFILE;
132}
133
134[a-zA-Z_][a-zA-Z0-9_]* {
135 yylval->sVal = rstrdup( yytext );
136 if( b.isKeyword( yylval->sVal ) )
137 return KEYWORD;
138 else if( b.isCond( yylval->sVal ) )
139 return CONDITION;
140 return UNDEF;
141}
142
143-?([1-9][0-9]*)|(0) {
144 yylval->iVal = strtol( yytext, NULL, 10 );
145 return INT;
146}
147
148(0\.0+)|(-?(([1-9][0-9]*)|(0))\.[0-9]*) {
149 yylval->fVal = strtof( yytext, NULL );
150 return FLOAT;
151}
152
153\" {
154 BEGIN( strdq );
155 sBuf.clear();
156 iStrDepth = 0;
157}
158<strdq>[^\\\n\"$()]+ {
159 sBuf += yytext;
160}
161<strdq>\$\$ {
162 sBuf += "$$";
163}
164<strdq>\$\( {
165 iStrDepth++; // TODO: Should this really count depth? I dunno...
166 sBuf += "$(";
167}
168<strdq>\\\) sBuf += "\\)";
169<strdq>\) {
170 if( iStrDepth > 0 )
171 iStrDepth--;
172 sBuf += ")";
173}
174<strdq>[$(] {
175 sBuf += yytext;
176}
177<strdq>\n {
178 build_error( yylloc, yyscanner, b, "newline encountered in string");
179}
180<strdq>\\n sBuf += "\n";
181<strdq>\\t sBuf += "\t";
182<strdq>\\r sBuf += "\r";
183<strdq>\\b sBuf += "\b";
184<strdq>\\f sBuf += "\f";
185<strdq>\\\\ sBuf += "\\";
186<strdq>\\\" sBuf += "\"";
187<strdq>\\\' sBuf += "\'";
188<strdq>\\\( sBuf += "(";
189<strdq>\\\` sBuf += "`";
190<strdq>\\. printf("Invalid escape sequence.\n");
191<strdq>\"[ \t\r\n]*\" {/* Ignore spaces between strings. */}
192<strdq>\" {
193 if( iStrDepth > 0 )
194 {
195 sBuf += "\"";
196 }
197 else
198 {
199 BEGIN( INITIAL );
200 yylval->sVal = fstrdup( sBuf );
201 return STRING;
202 }
203}
204
205<<EOF>> {
206 build_pop_buffer_state( yyscanner );
207 if( !YY_CURRENT_BUFFER )
208 yyterminate();
209 else
210 b.endInclude( yylloc );
211}
212
213. {
214 build_error( yylloc, yyscanner, b, "invalid character");
215}
216%%
diff --git a/src/build.y b/src/build.y
new file mode 100644
index 0000000..a6618e4
--- /dev/null
+++ b/src/build.y
@@ -0,0 +1,741 @@
1%defines
2%{ /* Prologue -- decls and stuff */
3 #include "buildparser.h"
4 #include "ast.h"
5void yyerror( YYLTYPE *locp, yyscan_t yyscanner, BuildParser &bld, const char *msg );
6%}
7/* Bison declerations */
8
9%parse-param { yyscan_t yyscanner }
10%parse-param { BuildParser &bld }
11%lex-param { yyscan_t yyscanner }
12%lex-param { BuildParser &bld }
13%pure-parser
14
15%locations
16
17%debug
18%error-verbose
19%name-prefix="build_"
20
21%union {
22 int iVal;
23 char *sVal;
24 float fVal;
25 bool bVal;
26}
27
28%token <sVal> STRING "string literal"
29%token <sVal> KEYWORD "keyword"
30%token <sVal> CONDITION "condition term"
31%token <sVal> VARIABLE "variable name"
32%token <sVal> FUNCTION "function name"
33%token <sVal> UNDEF "undefined identifier"
34%token <sVal> PROFILE "profile execute"
35%token <iVal> INT "integer literal"
36%token <fVal> FLOAT "floating point literal"
37%token <bVal> BOOL "boolean literal"
38
39%token TOK_TARGET "target"
40%token TOK_INPUT "input"
41%token TOK_OUTPUT "output"
42%token TOK_UNSET "unset"
43%token TOK_SET "set"
44%token TOK_CONDITION "condition"
45%token TOK_REQUIRES "requires"
46%token TOK_AUTO "auto"
47%token TOK_CONFIG "config"
48%token TOK_DISPLAY "display"
49%token TOK_TYPE "type"
50%token TOK_INT "int"
51%token TOK_FLOAT "float"
52%token TOK_BOOL "boolean"
53%token TOK_VERSION "version"
54%token TOK_STRING "string"
55%token TOK_DEFAULT "default"
56%token TOK_ALLOW "allow"
57%token TOK_RULE "rule"
58%token TOK_ACTION "action"
59%token TOK_PROFILE "profile"
60%token TOK_IF "if"
61%token TOK_THEN "then"
62%token TOK_ELSE "else"
63%token TOK_INCLUDE "include"
64%token TOK_ERROR "error"
65%token TOK_WARNING "warning"
66%token TOK_NOTICE "notice"
67%token TOK_CACHE "cache"
68%token TOK_ALWAYS "always"
69%token TOK_NEVER "never"
70%token TOK_GLOBAL "global"
71%token TOK_LOCAL "local"
72%token TOK_FOR "for"
73%token TOK_IN "in"
74%token TOK_DO "do"
75%token TOK_RETURN "return"
76%token TOK_FUNCTION "function"
77%token TOK_CONTINUE "continue"
78%token TOK_BREAK "break"
79%token TOK_VALUE "value"
80%token TOK_ALL "all"
81%token TOK_EXPORT "export"
82%token TOK_TAG "tag"
83%token TOK_NULL "null"
84
85%token OP_ADDSETP "+="
86%token OP_ADDSETR "<<"
87%token OP_CMPEQUAL "=="
88%token OP_INEQUAL "!="
89%token OP_LTEQUAL "<="
90%token OP_GTEQUAL ">="
91
92%token '(' ')' '{' '}' '[' ']' ',' ';' '=' '.' '<' '>' '+' '-' '*' '/'
93
94%right '=' OP_ADDSETP OPADDSETR
95%left OP_CMPEQUAL '<' '>' OP_INEQUAL OP_LTEQUAL OP_GTEQUAL '+' '-' '*' '/'
96%left '(' ')' '{' '}' '[' ']'
97%left IINEG IINOT
98
99%destructor { delete[] $$; } STRING
100%destructor { delete[] $$; } KEYWORD
101%destructor { delete[] $$; } CONDITION
102%destructor { delete[] $$; } VARIABLE
103%destructor { delete[] $$; } FUNCTION
104%destructor { delete[] $$; } UNDEF
105%destructor { delete[] $$; } PROFILE
106
107%% /* Grammar rules */
108
109/*
110 * root stuff
111 */
112
113root:
114// | root set
115 | root line_expr
116 | root unset
117 | root target
118 | root rule
119 | root config
120 | root root_if
121 | root root_for
122 | root include
123 | root notify
124 | root export
125 | root function_def
126 | root action_def
127 ;
128
129root_sub_exprs:
130// | root set
131 | root line_expr
132 | root unset
133 | root target
134 | root rule
135 | root config
136 | root root_if
137 | root root_for
138 | root include
139 | root notify
140 | root export
141 ;
142
143include: TOK_INCLUDE STRING ';' { bld.include( $2, yyscanner, &yylloc ); }
144 ;
145
146/*
147 * data related
148 */
149
150string: STRING { bld.xAst.addNode( AstNode::typeString, $1 ); }
151 ;
152
153int: INT { bld.xAst.addNode( AstNode::typeInt, $1 ); }
154 ;
155
156float: FLOAT { bld.xAst.addNode( AstNode::typeFloat, $1 ); }
157 ;
158
159bool: BOOL { bld.xAst.addNode( AstNode::typeBool, (bool)$1 ); }
160 ;
161
162null: TOK_NULL { bld.xAst.addNode( AstNode::typeNull ); }
163
164literal: string
165 | int
166 | float
167 | bool
168 | null
169 ;
170
171variable: UNDEF /*VARIABLE*/ { bld.xAst.addNode( AstNode::typeVariable, $1 ); }
172
173list_core:
174 | { bld.xAst.openBranch(); } expr
175 | list_core ',' { bld.xAst.openBranch(); } expr
176 ;
177
178list: '[' {
179 bld.xAst.addNode( AstNode::typeList );
180 } list_core ']' {
181 bld.xAst.closeNode();
182 }
183 ;
184
185value_mods:
186 | value_mods '.' function
187 ;
188
189value_core: variable
190 | literal
191 | function
192 | list
193 ;
194
195value: value_core value_mods
196 ;
197
198/*
199 * misc global things
200 */
201
202notify: TOK_ERROR STRING ';' { bld.xAst.addNode( AstNode::typeError, $2 ); }
203 | TOK_WARNING STRING ';' { bld.xAst.addNode( AstNode::typeWarning, $2 ); }
204 | TOK_NOTICE STRING ';' { bld.xAst.addNode( AstNode::typeNotice, $2 ); }
205 ;
206/*
207set_rhs: '=' { bld.xAst.addNode( AstNode::typeOpEq ); } value
208 | OP_ADDSETP { bld.xAst.addNode( AstNode::typeOpPlusEq ); } value
209 | OP_ADDSETR { bld.xAst.addNode( AstNode::typeOpPlusEqRaw ); } string
210 ;
211
212set: TOK_SET {
213 bld.xAst.addNode( AstNode::typeSet );
214 bld.xAst.openBranch();
215 } variable set_rhs ';' {
216 bld.xAst.closeNode();
217 }
218 ;*/
219
220unset: TOK_UNSET {
221 bld.xAst.addNode( AstNode::typeUnset );
222 bld.xAst.openBranch();
223 } variable ';' {
224 bld.xAst.closeNode();
225 }
226 ;
227
228export_rhs: '=' value
229 |
230 ;
231
232export: TOK_EXPORT {
233 bld.xAst.addNode( AstNode::typeExport );
234 bld.xAst.openBranch();
235 } variable export_rhs ';' {
236 bld.xAst.closeNode();
237 }
238 ;
239
240func_params:
241 | func_param_list
242 ;
243
244func_param_list: { bld.xAst.openBranch(); } value
245 | func_param_list ',' { bld.xAst.openBranch(); } value
246 ;
247
248function: UNDEF '(' {
249 bld.xAst.addNode( AstNode::typeFunction );
250 bld.xAst.openBranch();
251 bld.xAst.addNode( AstNode::typeString, $1 );
252 } func_params ')' {
253 bld.xAst.closeNode();
254 }
255 ;
256
257requires: TOK_REQUIRES {
258 bld.xAst.addNode( AstNode::typeRequires );
259 bld.xAst.openBranch();
260 } value ';' {
261 bld.xAst.closeNode();
262 }
263 ;
264
265type: TOK_STRING { bld.xAst.addNode( AstNode::typeTypeString ); }
266 | TOK_INT { bld.xAst.addNode( AstNode::typeTypeInt ); }
267 | TOK_FLOAT { bld.xAst.addNode( AstNode::typeTypeFloat ); }
268 | TOK_BOOL { bld.xAst.addNode( AstNode::typeTypeBool ); }
269 | TOK_VERSION { bld.xAst.addNode( AstNode::typeTypeVersion ); }
270 ;
271
272/*
273 * comparisons
274 */
275
276expr: value
277 | '(' expr ')'
278 | UNDEF '=' {
279 bld.xAst.addNode( AstNode::typeVariableRef, $1 );
280 } expr {
281 bld.xAst.addNode( AstNode::typeOpEq );
282 }
283 | UNDEF OP_ADDSETP {
284 bld.xAst.addNode( AstNode::typeVariableRef, $1 );
285 } expr {
286 bld.xAst.addNode( AstNode::typeOpPlusEq );
287 }
288 | expr OP_CMPEQUAL expr
289 {
290 bld.xAst.addNode( AstNode::typeCmpEq );
291 }
292 | expr '<' expr
293 {
294 bld.xAst.addNode( AstNode::typeCmpLt );
295 }
296 | expr '>' expr
297 {
298 bld.xAst.addNode( AstNode::typeCmpGt );
299 }
300 | expr OP_INEQUAL expr
301 {
302 bld.xAst.addNode( AstNode::typeCmpNe );
303 }
304 | expr OP_LTEQUAL expr
305 {
306 bld.xAst.addNode( AstNode::typeCmpLtEq );
307 }
308 | expr OP_GTEQUAL expr
309 {
310 bld.xAst.addNode( AstNode::typeCmpGtEq );
311 }
312 | expr '+' expr
313 {
314 bld.xAst.addNode( AstNode::typeOpPlus );
315 }
316 | expr '-' expr
317 {
318 bld.xAst.addNode( AstNode::typeOpMinus );
319 }
320 | expr '*' expr
321 {
322 bld.xAst.addNode( AstNode::typeOpMultiply );
323 }
324 | expr '/' expr
325 {
326 bld.xAst.addNode( AstNode::typeOpDivide );
327 }
328 | '-' expr %prec IINEG
329 {
330 bld.xAst.addNode( AstNode::typeOpNegate );
331 }
332 | '!' expr %prec IINOT
333 {
334 bld.xAst.addNode( AstNode::typeOpNot );
335 }
336 ;
337
338line_expr: {
339 bld.xAst.addNode( AstNode::typeExpr );
340 bld.xAst.openBranch();
341 } expr ';'
342 {
343 bld.xAst.closeNode();
344 }
345 ;
346
347if_core: TOK_IF {
348 bld.xAst.addNode( AstNode::typeIf );
349 bld.xAst.openBranch();
350// bld.xAst.addNode( AstNode::typeExpr );
351// bld.xAst.openBranch();
352 } expr TOK_THEN {
353// bld.xAst.closeNode();
354 bld.xAst.openBranch();
355 }
356 ;
357
358else: TOK_ELSE { bld.xAst.openBranch(); }
359 ;
360
361root_if: if_core '{' root_sub_exprs '}' root_else { bld.xAst.closeNode(); }
362 ;
363
364root_else:
365 | else '{' root_sub_exprs '}'
366 | else root_if
367 ;
368
369target_if: if_core '{' target_exprs '}' target_else { bld.xAst.closeNode(); }
370 ;
371
372target_else:
373 | else '{' target_exprs '}'
374 | else target_if
375 ;
376
377rule_if: if_core '{' rule_exprs '}' rule_else { bld.xAst.closeNode(); }
378 ;
379
380rule_else:
381 | else '{' rule_exprs '}'
382 | else rule_if
383 ;
384
385function_if: if_core '{' function_exprs '}' function_else
386 { bld.xAst.closeNode(); }
387 ;
388
389function_else:
390 | else '{' function_exprs '}'
391 | else function_if
392 ;
393
394/*
395 * loops
396 */
397
398for_base: TOK_FOR {
399 bld.xAst.addNode( AstNode::typeFor );
400 bld.xAst.openBranch();
401 } variable TOK_IN {
402 bld.xAst.openBranch();
403 } value TOK_DO {
404 bld.xAst.openBranch();
405 }
406 ;
407
408root_for: for_base '{' root_sub_exprs '}' { bld.xAst.closeNode(); }
409 ;
410
411target_for: for_base '{' target_exprs '}' { bld.xAst.closeNode(); }
412 ;
413
414rule_for: for_base '{' rule_exprs '}' { bld.xAst.closeNode(); }
415 ;
416
417function_for: for_base '{' function_exprs '}' { bld.xAst.closeNode(); }
418 ;
419
420/*
421 * functions
422 */
423
424function_def: TOK_FUNCTION UNDEF {
425 bld.xAst.addNode( AstNode::typeFunctionDef );
426 bld.xAst.openBranch();
427 bld.xAst.addNode( AstNode::typeString, $2 );
428 bld.xAst.openBranch();
429 } '(' param_defs ')' {
430 bld.xAst.openBranch();
431 } '{' function_exprs '}' {
432 bld.xAst.closeNode();
433 }
434 ;
435
436param_defs:
437 | param_def_list
438 ;
439
440param_def_list: variable
441 | param_def_list ',' variable
442 ;
443
444function_exprs:
445// | function_exprs function ';'
446// | function_exprs set
447 | function_exprs unset
448 | function_exprs line_expr
449 | function_exprs export
450 | function_exprs notify
451 | function_exprs function_if
452 | function_exprs function_for
453 | function_exprs return
454 | function_exprs process_target
455 ;
456
457return: TOK_RETURN {
458 bld.xAst.addNode( AstNode::typeReturn );
459 bld.xAst.openBranch();
460 } expr {
461 bld.xAst.closeNode();
462 } ';'
463 ;
464
465/*
466 * Actions, they're basically functions, no parameters
467 */
468
469action_def: TOK_ACTION STRING {
470 bld.xAst.addNode( AstNode::typeActionDef );
471 bld.xAst.openBranch();
472 bld.xAst.addNode( AstNode::typeString, $2 );
473 bld.xAst.openBranch();
474 } '{' function_exprs '}' {
475 bld.xAst.closeNode();
476 }
477 ;
478
479/*
480 * profiles
481 */
482
483profile: TOK_PROFILE {
484 bld.xAst.addNode( AstNode::typeProfile );
485 bld.xAst.openBranch();
486 } string {
487 bld.xAst.openBranch();
488 } '{' profile_exprs '}' {
489 bld.xAst.closeNode();
490 } /* in-line function */
491 ;
492
493profile_exprs:
494// | profile_exprs function ';'
495// | profile_exprs set
496 | profile_exprs unset
497 | profile_exprs line_expr
498 | profile_exprs export
499 | profile_exprs notify
500 | profile_exprs function_if
501 | profile_exprs function_for
502 | profile_exprs return
503 | profile_exprs process_target
504 | profile_exprs condition
505 ;
506/*
507 * targets
508 */
509
510target: TOK_TARGET {
511 bld.xAst.addNode( AstNode::typeTarget );
512 bld.xAst.openBranch();
513 } expr {
514 bld.xAst.openBranch();
515 } '{' target_exprs '}' {
516 bld.xAst.closeNode();
517 }
518 ;
519
520target_exprs:
521// | target_exprs set
522 | target_exprs unset
523 | target_exprs line_expr
524 | target_exprs export
525 | target_exprs target_input
526 | target_exprs requires
527 | target_exprs profile
528 | target_exprs target_if
529 | target_exprs target_for
530 | target_exprs notify
531 | target_exprs target_rule
532 | target_exprs tag
533 | target_exprs display
534 ;
535
536target_input: TOK_INPUT {
537 bld.xAst.addNode( AstNode::typeInput );
538 bld.xAst.openBranch();
539 } expr ';' {
540 bld.xAst.closeNode();
541 }
542 ;
543
544target_rule: TOK_RULE {
545 bld.xAst.addNode( AstNode::typeRule );
546 bld.xAst.openBranch();
547 } string ';' {
548 bld.xAst.closeNode();
549 }
550 ;
551
552condition: TOK_CONDITION CONDITION ';' {
553 bld.xAst.addNode( AstNode::typeCondition, $2 );
554 }
555 | TOK_CONDITION TOK_ALWAYS ';'{
556 bld.xAst.addNode( AstNode::typeCondition, "always" );
557 }
558 | TOK_CONDITION TOK_NEVER ';'{
559 bld.xAst.addNode( AstNode::typeCondition, "never" );
560 }
561 ;
562
563/*
564 * rules
565 */
566
567rule: TOK_RULE {
568 bld.xAst.addNode( AstNode::typeRuleDef );
569 bld.xAst.openBranch();
570 } string {
571 bld.xAst.openBranch();
572 } '{' rule_exprs '}' {
573 bld.xAst.closeNode();
574 }
575 ;
576
577rule_exprs:
578 | rule_exprs rule_input
579 | rule_exprs output
580 | rule_exprs requires
581 | rule_exprs profile
582 | rule_exprs rule_if
583 | rule_exprs rule_for
584 | rule_exprs notify
585 | rule_exprs display
586 | rule_exprs tag
587// | rule_exprs set
588 ;
589
590rule_input_func: function
591 | STRING {
592 /* In this case, when the input is just a string,
593 lets actually turn it into a call to the matches function.
594 */
595 bld.xAst.addNode( AstNode::typeFunction );
596 bld.xAst.openBranch();
597 bld.xAst.addNode( AstNode::typeString, "matches" );
598 bld.xAst.openBranch();
599 bld.xAst.addNode( AstNode::typeString, $1 );
600 bld.xAst.closeNode();
601 }
602/* | string */
603 ;
604
605rule_input: TOK_INPUT {
606 bld.xAst.addNode( AstNode::typeInput );
607 bld.xAst.openBranch();
608 } rule_input_func ';' {
609 bld.xAst.closeNode();
610 }
611 ;
612
613output: TOK_OUTPUT {
614 bld.xAst.addNode( AstNode::typeOutput );
615 bld.xAst.openBranch();
616 } value ';' {
617 bld.xAst.closeNode();
618 }
619 ;
620
621/*
622 * config
623 */
624config: TOK_CONFIG {
625 bld.xAst.addNode( AstNode::typeConfig );
626 bld.xAst.openBranch();
627 } string {
628 bld.xAst.openBranch();
629 } '{' config_exprs '}' {
630 bld.xAst.closeNode();
631 }
632 | TOK_AUTO TOK_CONFIG {
633 bld.xAst.addNode( AstNode::typeAutoConfig );
634 bld.xAst.openBranch();
635 } string {
636 bld.xAst.openBranch();
637 } '{' config_exprs '}' {
638 bld.xAst.closeNode();
639 }
640 | TOK_GLOBAL TOK_CONFIG {
641 bld.xAst.addNode( AstNode::typeGlobalConfig );
642 bld.xAst.openBranch();
643 } string {
644 bld.xAst.openBranch();
645 } '{' config_exprs '}' {
646 bld.xAst.closeNode();
647 }
648 ;
649
650config_exprs:
651 | config_exprs display
652 | config_exprs config_type
653 | config_exprs default
654 | config_exprs value_key
655 | config_exprs allow
656 | config_exprs cache
657 ;
658
659display: TOK_DISPLAY STRING ';' {
660 bld.xAst.addNode( AstNode::typeDisplay, $2 );
661 }
662 ;
663
664config_type: TOK_TYPE {
665 bld.xAst.addNode( AstNode::typeType );
666 bld.xAst.openBranch();
667 } type ';' {
668 bld.xAst.closeNode();
669 }
670 ;
671
672default: TOK_DEFAULT {
673 bld.xAst.addNode( AstNode::typeDefault );
674 bld.xAst.openBranch();
675 } literal ';' {
676 bld.xAst.closeNode();
677 }
678 ;
679
680value_key_val: value ';'
681 | '{' function_exprs '}' /* inline function */
682
683value_key: TOK_VALUE {
684 bld.xAst.addNode( AstNode::typeValue );
685 bld.xAst.openBranch();
686 } value_key_val {
687 bld.xAst.closeNode();
688 }
689 ;
690
691allow: TOK_ALLOW {
692 bld.xAst.addNode( AstNode::typeAllow );
693 bld.xAst.openBranch();
694 } value ';' {
695 bld.xAst.closeNode();
696 }
697 ;
698
699cache: TOK_CACHE TOK_ALWAYS ';'
700 { bld.xAst.addNode( AstNode::typeCache, true ); }
701 | TOK_CACHE TOK_NEVER ';'
702 { bld.xAst.addNode( AstNode::typeCache, false ); }
703 ;
704
705/*
706 * target/profile execute
707 */
708process_target: PROFILE
709 {
710 bld.xAst.addNode( AstNode::typeProcessTarget );
711 bld.xAst.openBranch();
712 bld.xAst.addNode( AstNode::typeString, $1 );
713 bld.xAst.openBranch();
714 } value ';' {
715 bld.xAst.closeNode();
716 }
717 ;
718
719tag: TOK_TAG
720 {
721 bld.xAst.addNode( AstNode::typeTag );
722 bld.xAst.openBranch();
723 } value ';' {
724 bld.xAst.closeNode();
725 }
726 ;
727
728
729%%
730
731/* Epilogue -- whatever you want, functions mainly */
732
733void build_error( YYLTYPE *locp, yyscan_t, BuildParser &bld, const char *msg )
734{
735 bld.error(
736 locp->first_line, locp->last_line,
737 locp->first_column, locp->last_column,
738 msg
739 );
740}
741
diff --git a/src/buildparser.cpp b/src/buildparser.cpp
new file mode 100644
index 0000000..e391523
--- /dev/null
+++ b/src/buildparser.cpp
@@ -0,0 +1,127 @@
1#include "buildparser.h"
2#include "ast.h"
3#include "build.yy.h"
4
5#include "bu/sio.h"
6using Bu::sio;
7
8BuildParser::BuildParser( Ast &rAst ) :
9 xAst( rAst )
10{
11 lIncludePaths.append("./");
12}
13
14BuildParser::~BuildParser()
15{
16}
17
18int build_parse( yyscan_t yyscanner, BuildParser &bld );
19
20void BuildParser::load( const Bu::FString &sFile )
21{
22 yyscan_t scanner;
23
24 sFilename.push( sFile );
25 FILE *fIn = fopen( sFile.getStr(), "rt" );
26 build_lex_init( &scanner );
27 // build_set_debug( true, scanner );
28 build_set_in( fIn, scanner );
29
30 build_parse( scanner, *this );
31
32 build_lex_destroy( scanner );
33 fclose( fIn );
34
35 // Bu::sio << xAst;
36}
37
38bool BuildParser::isKeyword( const Bu::FString &sStr )
39{
40 if( sStr == "important" )
41 return true;
42 if( sStr == "normal" )
43 return true;
44 if( sStr == "hidden" )
45 return true;
46 if( sStr == "autogenerated" )
47 return true;
48 return false;
49}
50
51bool BuildParser::isCond( const Bu::FString &sStr )
52{
53 if( sStr == "filetime" )
54 return true;
55 if( sStr == "always" )
56 return true;
57 if( sStr == "never" )
58 return true;
59 return false;
60}
61
62void BuildParser::include( const Bu::FString &sStr, void *scanner, YYLTYPE *loc )
63{
64 for( StrList::iterator pi = lIncludePaths.begin(); pi; pi++ )
65 {
66 FILE *fIn = fopen( (*pi + sStr).getStr(), "rt" );
67 if( fIn == NULL )
68 {
69 continue;
70 }
71 sFilename.push( sStr );
72 sLocation.push( *loc );
73 loc->first_line = loc->last_line = 1;
74 loc->first_column = loc->last_column = 0;
75 build_push_buffer_state(
76 build__create_buffer( fIn, YY_READ_BUF_SIZE, scanner ),
77 scanner
78 );
79 Bu::FString::const_iterator i = sStr.find('/');
80 if( i )
81 {
82 for(;;)
83 {
84 Bu::FString::const_iterator j = i.find('/');
85 if( !j )
86 break;
87 i = j+1;
88 }
89 sio << "Hey, found it from here: " << sStr.getSubStr( sStr.begin(), i ) << sio.nl;
90 xAst.addNode( AstNode::typePushPrefix, sStr.getSubStr( sStr.begin(), i ) );
91 }
92 else
93 {
94 xAst.addNode( AstNode::typePushPrefix, "" );
95 }
96 return;
97 }
98 Bu::FString msg("Could not open include file: ");
99 msg += sStr;
100 error(
101 loc->first_line, loc->last_line,
102 loc->first_column, loc->last_column,
103 msg
104 );
105}
106
107void BuildParser::endInclude( YYLTYPE *loc )
108{
109 sFilename.pop();
110 memcpy( loc, &sLocation.peek(), sizeof(YYLTYPE) );
111 sLocation.pop();
112 xAst.addNode( AstNode::typePopPrefix );
113}
114
115void BuildParser::error( int iLine1, int iLine2, int iCol1, int iCol2,
116 const Bu::FString &sMsg )
117{
118 throw Bu::ExceptionBase("%s: %d-%d:%d-%d: %s",
119 sFilename.peek().getStr(), iLine1, iLine2, iCol1, iCol2, sMsg.getStr()
120 );
121}
122
123void BuildParser::addIncludePath( const Bu::FString &sPath )
124{
125 lIncludePaths.append( sPath + "/" );
126}
127
diff --git a/src/buildparser.h b/src/buildparser.h
new file mode 100644
index 0000000..8e2af6c
--- /dev/null
+++ b/src/buildparser.h
@@ -0,0 +1,40 @@
1#ifndef BUILD_PARSER_H
2#define BUILD_PARSER_H
3
4#include "build.tab.h"
5
6#include "bu/stack.h"
7#include "bu/fstring.h"
8#include "types.h"
9
10class BuildParser
11{
12public:
13 BuildParser( class Ast &rAst );
14 virtual ~BuildParser();
15
16 void load( const Bu::FString &sFile );
17
18 bool isKeyword( const Bu::FString &sStr );
19 bool isCond( const Bu::FString &sStr );
20 void include( const Bu::FString &sStr, void *scanner, YYLTYPE *loc );
21 void endInclude( YYLTYPE *loc );
22
23 void error( int iLine1, int iLine2, int iCol1, int iCol2,
24 const Bu::FString &sMsg );
25
26 class Ast &xAst;
27
28 void addIncludePath( const Bu::FString &sPath );
29
30private:
31 Bu::Stack<Bu::FString> sFilename;
32 Bu::Stack<YYLTYPE> sLocation;
33 StrList lIncludePaths;
34};
35
36typedef void * yyscan_t;
37#define YY_DECL int build_lex( YYSTYPE *yylval_param, YYLTYPE *yylloc_param, yyscan_t yyscanner, BuildParser &b )
38YY_DECL;
39
40#endif
diff --git a/src/condition.cpp b/src/condition.cpp
new file mode 100644
index 0000000..ec87c0e
--- /dev/null
+++ b/src/condition.cpp
@@ -0,0 +1,10 @@
1#include "condition.h"
2
3Condition::Condition()
4{
5}
6
7Condition::~Condition()
8{
9}
10
diff --git a/src/condition.h b/src/condition.h
new file mode 100644
index 0000000..2e9b26a
--- /dev/null
+++ b/src/condition.h
@@ -0,0 +1,16 @@
1#ifndef CONDITION_H
2#define CONDITION_H
3
4class Condition
5{
6public:
7 Condition();
8 virtual ~Condition();
9
10 virtual bool shouldExec( class Runner &r, class Target &rTarget )=0;
11 virtual Condition *clone()=0;
12
13private:
14};
15
16#endif
diff --git a/src/conditionalways.cpp b/src/conditionalways.cpp
new file mode 100644
index 0000000..077b5b5
--- /dev/null
+++ b/src/conditionalways.cpp
@@ -0,0 +1,21 @@
1#include "conditionalways.h"
2#include "target.h"
3
4ConditionAlways::ConditionAlways()
5{
6}
7
8ConditionAlways::~ConditionAlways()
9{
10}
11
12bool ConditionAlways::shouldExec( class Runner &/*r*/, Target &/*rTarget*/ )
13{
14 return true;
15}
16
17Condition *ConditionAlways::clone()
18{
19 return new ConditionAlways();
20}
21
diff --git a/src/conditionalways.h b/src/conditionalways.h
new file mode 100644
index 0000000..1eeeb71
--- /dev/null
+++ b/src/conditionalways.h
@@ -0,0 +1,16 @@
1#ifndef CONDITION_ALWAYS_H
2#define CONDITION_ALWAYS_H
3
4#include "condition.h"
5
6class ConditionAlways : public Condition
7{
8public:
9 ConditionAlways();
10 virtual ~ConditionAlways();
11
12 virtual bool shouldExec( class Runner &r, class Target &rTarget );
13 virtual Condition *clone();
14};
15
16#endif
diff --git a/src/conditionfiletime.cpp b/src/conditionfiletime.cpp
new file mode 100644
index 0000000..224caf1
--- /dev/null
+++ b/src/conditionfiletime.cpp
@@ -0,0 +1,73 @@
1#include "conditionfiletime.h"
2#include "target.h"
3
4#include <sys/types.h>
5#include <sys/stat.h>
6#include <unistd.h>
7
8#include <bu/sio.h>
9using namespace Bu;
10
11ConditionFileTime::ConditionFileTime()
12{
13}
14
15ConditionFileTime::~ConditionFileTime()
16{
17}
18
19bool ConditionFileTime::shouldExec( class Runner &r, Target &rTarget )
20{
21 for( StrList::const_iterator j = rTarget.getOutputList().begin(); j; j++ )
22 {
23 if( access( (*j).getStr(), F_OK ) )
24 {
25 //sio << "-- Target processed because '" << *j << "' doesn't exist."
26 // << sio.nl;
27 // Output doesn't exist
28 return true;
29 }
30 }
31
32 time_t tOut = 0;
33 struct stat s;
34 for( StrList::const_iterator j = rTarget.getOutputList().begin();
35 j; j++ )
36 {
37 stat( (*j).getStr(), &s );
38 if( tOut == 0 || tOut > s.st_mtime )
39 {
40 tOut = s.st_mtime;
41 }
42 }
43 for( StrList::const_iterator j = rTarget.getInputList().begin();
44 j; j++ )
45 {
46 stat( (*j).getStr(), &s );
47 if( tOut < s.st_mtime )
48 {
49 //sio << "-- Target processed because '" << *j
50 // << "' is newer than output." << sio.nl;
51 return true;
52 }
53 }
54 rTarget.buildRequires( r );
55 for( StrList::const_iterator j = rTarget.getRequiresList().begin();
56 j; j++ )
57 {
58 stat( (*j).getStr(), &s );
59 if( tOut < s.st_mtime )
60 {
61 //sio << "-- Target processed because '" << *j
62 // << "' is newer than output." << sio.nl;
63 return true;
64 }
65 }
66 return false;
67}
68
69Condition *ConditionFileTime::clone()
70{
71 return new ConditionFileTime();
72}
73
diff --git a/src/conditionfiletime.h b/src/conditionfiletime.h
new file mode 100644
index 0000000..6fb2e9d
--- /dev/null
+++ b/src/conditionfiletime.h
@@ -0,0 +1,16 @@
1#ifndef CONDITION_FILE_TIME_H
2#define CONDITION_FILE_TIME_H
3
4#include "condition.h"
5
6class ConditionFileTime : public Condition
7{
8public:
9 ConditionFileTime();
10 virtual ~ConditionFileTime();
11
12 virtual bool shouldExec( class Runner &r, class Target &rTarget );
13 virtual Condition *clone();
14};
15
16#endif
diff --git a/src/conditionnever.cpp b/src/conditionnever.cpp
new file mode 100644
index 0000000..1ab4375
--- /dev/null
+++ b/src/conditionnever.cpp
@@ -0,0 +1,21 @@
1#include "conditionnever.h"
2#include "target.h"
3
4ConditionNever::ConditionNever()
5{
6}
7
8ConditionNever::~ConditionNever()
9{
10}
11
12bool ConditionNever::shouldExec( class Runner &/*r*/, Target &/*rTarget*/ )
13{
14 return false;
15}
16
17Condition *ConditionNever::clone()
18{
19 return new ConditionNever();
20}
21
diff --git a/src/conditionnever.h b/src/conditionnever.h
new file mode 100644
index 0000000..b7e5e92
--- /dev/null
+++ b/src/conditionnever.h
@@ -0,0 +1,16 @@
1#ifndef CONDITION_NEVER_H
2#define CONDITION_NEVER_H
3
4#include "condition.h"
5
6class ConditionNever : public Condition
7{
8public:
9 ConditionNever();
10 virtual ~ConditionNever();
11
12 virtual bool shouldExec( class Runner &r, class Target &rTarget );
13 virtual Condition *clone();
14};
15
16#endif
diff --git a/src/context.cpp b/src/context.cpp
new file mode 100644
index 0000000..c257a75
--- /dev/null
+++ b/src/context.cpp
@@ -0,0 +1,524 @@
1#include "context.h"
2#include "target.h"
3#include "rule.h"
4#include "function.h"
5#include "runner.h"
6#include "action.h"
7#include "profile.h"
8#include "view.h"
9
10#include "functionreplace.h"
11#include "functionexists.h"
12#include "functionfiles.h"
13#include "functionexecute.h"
14#include "functionmatches.h"
15#include "functiontostring.h"
16#include "functionunlink.h"
17#include "functiontargets.h"
18#include "functiondirs.h"
19#include "functiongetmakedeps.h"
20#include "functionfilename.h"
21#include "functiondirname.h"
22
23#include <bu/process.h>
24#include <bu/sio.h>
25using namespace Bu;
26
27Context::Context() :
28 pView( NULL )
29{
30 addFunction( new FunctionReplace() );
31 addFunction( new FunctionExists() );
32 addFunction( new FunctionFiles() );
33 addFunction( new FunctionExecute() );
34 addFunction( new FunctionMatches() );
35 addFunction( new FunctionToString() );
36 addFunction( new FunctionUnlink() );
37 addFunction( new FunctionTargets() );
38 addFunction( new FunctionDirs() );
39 addFunction( new FunctionGetMakeDeps() );
40 addFunction( new FunctionFileName() );
41 addFunction( new FunctionDirName() );
42 pushScope();
43}
44
45Context::~Context()
46{
47}
48
49void Context::addTarget( Target *pTarget )
50{
51 for( StrList::const_iterator i = pTarget->getOutputList().begin(); i; i++ )
52 {
53 hTarget.insert( (*i).getStr(), pTarget );
54 }
55}
56
57void Context::addRule( Rule *pRule )
58{
59 hRule.insert( pRule->getName(), pRule );
60}
61
62void Context::addFunction( Function *pFunction )
63{
64 pFunction->setContext( this );
65 hFunction.insert( pFunction->getName(), pFunction );
66}
67
68void Context::addVariable( const Bu::FString &sName, const Variable &vValue )
69{
70 for( ScopeStack::iterator i = sVars.begin(); i; i++ )
71 {
72 if( (*i).has( sName ) )
73 {
74// sio << "Replacing higher scope variable \"" << sName << "\" with value \"" << (*i).get( sName ) << "\" with new value \"" << vValue << "\"" << sio.nl;
75 (*i).insert( sName, vValue );
76 return;
77 }
78 }
79 sVars.first().insert( sName, vValue );
80}
81
82void Context::addAction( Action *pAction )
83{
84 hAction.insert( pAction->getName(), pAction );
85}
86
87Action *Context::getAction( const Bu::FString &sName )
88{
89 return hAction.get( sName );
90}
91
92void Context::addTargetToTag( Target *pTarget, const Bu::FString &sTag )
93{
94 if( !hTag.has( sTag ) )
95 {
96 hTag.insert( sTag, TargetList() );
97 }
98 hTag.get( sTag ).append( pTarget );
99}
100
101void Context::addTargetToTags( Target *pTarget, const StrList &sTags )
102{
103 for( StrList::const_iterator i = sTags.begin(); i; i++ )
104 {
105 addTargetToTag( pTarget, *i );
106 }
107}
108
109TargetList &Context::getTag( const Bu::FString &sTag )
110{
111 return hTag.get( sTag );
112}
113
114Variable &Context::getVariable( const Bu::FString &sName )
115{
116 for( ScopeStack::iterator i = sVars.begin(); i; i++ )
117 {
118 if( (*i).has( sName ) )
119 {
120 return (*i).get( sName );
121 }
122 }
123 throw Bu::ExceptionBase("No such variable.");
124}
125
126void Context::delVariable( const Bu::FString &sName )
127{
128 for( ScopeStack::iterator i = sVars.begin(); i; i++ )
129 {
130 if( (*i).has( sName ) )
131 {
132 (*i).erase( sName );
133 }
134 }
135}
136
137void Context::pushScope()
138{
139 VarHash h;
140 if( !sVars.isEmpty() )
141 h = sVars.peek();
142 sVars.push( h );
143}
144
145void Context::pushScope( const VarHash &hNewVars )
146{
147 VarHash h = hNewVars;
148 if( !sVars.isEmpty() )
149 {
150 for( VarHash::iterator i = sVars.peek().begin(); i; i++ )
151 {
152 if( !h.has( i.getKey() ) )
153 h.insert( i.getKey(), i.getValue() );
154 }
155 }
156 sVars.push( h );
157}
158
159VarHash &Context::getScope()
160{
161 return sVars.first();
162}
163
164void Context::popScope()
165{
166 sVars.pop();
167}
168
169Variable Context::call( const Bu::FString &sName, Variable &input,
170 VarList lParams )
171{
172 if( !hFunction.has( sName ) )
173 {
174 throw Bu::ExceptionBase("Unknown function called: %s", sName.getStr() );
175 }
176 return hFunction.get( sName )->call( input, lParams );
177}
178
179#include <bu/sio.h>
180using namespace Bu;
181Bu::FString Context::expand( const Bu::FString &sInS )
182{
183 Bu::FString sRet;
184 Bu::FString sIn = sInS;
185
186 for( int iPass = 0; iPass < 2; iPass++ )
187 {
188 Bu::FString::const_iterator b = sIn.begin();
189 sRet.clear();
190 for(;;)
191 {
192 Bu::FString::const_iterator e = b.find('$');
193 if( !e )
194 {
195 sRet.append( b );
196 break;
197 }
198 sRet.append( b, e );
199 b = e+1;
200 if( !b )
201 {
202 sRet.append('$');
203 }
204 else if( *b == '{' )
205 {
206 b++;
207 e = b.find('}');
208 Bu::FString sVar( b, e );
209 try
210 {
211 sRet.append( getVariable( sVar ).toString() );
212 } catch(...)
213 {
214 // TODO: This may be handy debugging later...
215 //sio << "No variable named " << sVar << sio.nl;
216 //sio << "Vars: " << sVars << sio.nl << sio.nl;
217 }
218 b = e+1;
219 }
220 else if( *b == '(' && iPass == 1 )
221 {
222 b++;
223 e = b.find(')');
224 Bu::FString sCmd( b, e );
225 Bu::FString sBuf;
226 try
227 {
228 //sio << "Executing command: >>>" << sCmd << "<<<" << sio.nl;
229 Process p( Process::StdOut, "/bin/bash", "/bin/bash", "-c", sCmd.getStr(), NULL );
230 while( p.isRunning() )
231 {
232 char buf[4096];
233 sBuf.append( buf, p.read( buf, 4096 ) );
234 }
235 sRet.append( sBuf );
236 } catch(...)
237 {
238 // TODO: This may be handy debugging later...
239 //sio << "No variable named " << sVar << sio.nl;
240 //sio << "Vars: " << sVars << sio.nl << sio.nl;
241 }
242 b = e+1;
243 }
244 else
245 {
246 // Not a match, uh, just output the $ for now...
247 sRet.append('$');
248 }
249 }
250
251 sIn = sRet;
252 }
253 return sRet;
254}
255
256Target *Context::getTarget( const Bu::FString &sOutput )
257{
258 return hTarget.get( sOutput );
259}
260
261TargetList Context::getExplicitTargets()
262{
263 TargetList lRet;
264 for( TargetHash::iterator i = hTarget.begin(); i; i++ )
265 {
266 if( (*i)->isExplicit() )
267 lRet.append( *i );
268 }
269 return lRet;
270}
271
272void Context::buildTargetTree( Runner &r )
273{
274 TargetList lTargets = hTarget.getValues();
275
276 for( TargetList::iterator i = lTargets.begin(); i; i++ )
277 {
278 // I believe we only want to autogenerate targets for explicit targets
279 // that have rules defined.
280 if( !(*i)->isExplicit() || !(*i)->hasRule() )
281 continue;
282
283 StrList lNewIns; // The new "changed" inputs for this target
284
285 Rule *pMaster = hRule.get( (*i)->getRule() );
286
287 for( StrList::const_iterator j = (*i)->getInputList().begin(); j; j++ )
288 {
289 if( pMaster->ruleMatches( r, *j ) )
290 {
291 lNewIns.append( *j );
292 }
293
294 if( hTarget.has( *j ) )
295 {
296 // Find the existing dependancy
297 lNewIns.append( *j );
298 }
299 //else
300 //{
301 buildTargetTree( r, *i, *j, pMaster, lNewIns );
302 //}
303 }
304
305 pMaster->prepTarget( *i );
306 (*i)->resetInputList( lNewIns );
307 }
308 //sio << "Building dependancies: " << Fmt(3) << 0 << "%\r" << sio.flush;
309 //int iSize = hTarget.getSize(), iCur = 0;
310 for( TargetHash::iterator i = hTarget.begin(); i; i++ )
311 {
312 // Before we can take a look at the requirements, we need to build
313 // them...
314 // (*i)->buildRequires( r );
315
316 // For every target we have to examine both it's inputs and it's
317 // additional requirements. Inputs first
318 StrList lDeps( (*i)->getInputList() );
319 lDeps += (*i)->getRequiresList();
320 for( StrList::const_iterator j = lDeps.begin(); j; j++ )
321 {
322 try
323 {
324 (*i)->addDep( hTarget.get( *j ) );
325 }
326 catch(...)
327 {
328 }
329 }
330 //iCur++;
331 // sio << "Building dependancies: " << Fmt(3) << (iCur*100/iSize) << "%\r" << sio.flush;
332 (*i)->collapseDeps();
333 }
334// sio << sio.nl;
335
336 for( TargetHash::iterator i = hTarget.begin(); i; i++ )
337 {
338 if( !(*i)->isExplicit() )
339 continue;
340 (*i)->setDepCount();
341 (*i)->resetRun( false );
342 }
343}
344
345void Context::buildTargetTree( class Runner &r, class Target * /*pTarget*/, const Bu::FString &sInput, Rule *pMaster, StrList &lNewIns )
346{
347 Target *pNewTarget = NULL;
348 for( RuleHash::iterator i = hRule.begin(); i; i++ )
349 {
350 if( (*i)->hasOutputs() && (*i)->ruleMatches( r, sInput ) )
351 {
352 pNewTarget = (*i)->createTarget( r, sInput );
353
354 Bu::Hash<ptrdiff_t, bool> hDone;
355 for( StrList::const_iterator oi =
356 pNewTarget->getOutputList().begin(); oi; oi++ )
357 {
358 try
359 {
360 Target *pOver = hTarget.get( *oi );
361 if( hDone.has( (ptrdiff_t)pOver ) )
362 continue;
363 hDone.insert( (ptrdiff_t)pOver, true );
364 if( !pOver->isExplicit() )
365 {
366 delete pNewTarget;
367 pNewTarget = pOver;
368 break;
369 }
370 pOver->mergeUnder( pNewTarget );
371 delete pNewTarget;
372// sio << "Delete: " << Fmt::ptr() << (ptrdiff_t)pNewTarget << sio.nl;
373 pNewTarget = pOver;
374 break;
375 }
376 catch(...)
377 {
378 }
379 }
380
381 // We actually want to add this either way, if the merge added new
382 // outputs, then we need to take them into account.
383 addTarget( pNewTarget );
384 addTargetToTags( pNewTarget, (*i)->getTagList() );
385
386 // We have created a new target (or not, either way, we need to
387 // check if it matches.)
388 for( StrList::const_iterator m =
389 pNewTarget->getOutputList().begin(); m; m++ )
390 {
391 // Does the new output match the master rule?
392 if( pMaster->ruleMatches( r, (*m) ) )
393 {
394 lNewIns.append( (*m) );
395
396// sio << "What?" << sio.nl;
397 // These relationships are difficult to pick up on except
398 // that one target was created by the other, I suppose.
399 // Anyway, that means that we need to add this while we
400 // can.
401// pTarget->addDep( pNewTarget );
402 }
403 // else
404 // {
405 buildTargetTree( r, pNewTarget, *m, pMaster, lNewIns );
406 // }
407 }
408
409 return;
410 }
411 }
412 if( !pNewTarget )
413 {
414 //sio << "Incomplete tree created, trying to find purpose for \""
415 // << sInput << "\"." << sio.nl;
416 return;
417 }
418}
419
420void Context::attachDefaults()
421{
422 for( TargetHash::iterator i = hTarget.begin(); i; i++ )
423 {
424 if( !(*i)->hasProfile("clean") )
425 {
426 (*i)->addProfile( Profile::genDefaultClean() );
427 }
428 }
429}
430
431void Context::genDefaultActions()
432{
433 if( !hAction.has("all") )
434 {
435 addAction( Action::genDefaultAll() );
436 }
437 if( !hAction.has("clean") )
438 {
439 addAction( Action::genDefaultClean() );
440 }
441 if( !hAction.has("default") )
442 {
443 addAction( Action::genDefaultDefault() );
444 }
445}
446
447void Context::writeTargetDot()
448{
449 Bu::Hash<ptrdiff_t, bool> hDone;
450 sio << "digraph {" << sio.nl
451 << "\trankdir=LR;" << sio.nl;
452 for( TargetHash::iterator i = hTarget.begin(); i; i++ )
453 {
454 if( hDone.has( (ptrdiff_t)*i ) )
455 continue;
456 hDone.insert( (ptrdiff_t)*i, true );
457 for( StrList::const_iterator l = (*i)->getOutputList().begin();
458 l; l++ )
459 {
460 for( StrList::const_iterator k = (*i)->getInputList().begin();
461 k; k++ )
462 {
463 sio << "\t\"" << *k << "\" -> \""
464 << *l << "\";" << sio.nl;
465 }
466 for( StrList::const_iterator k = (*i)->getRequiresList().begin();
467 k; k++ )
468 {
469 sio << "\t\"" << *k << "\" -> \""
470 << *l << "\" [color=red];" << sio.nl;
471 }
472 }
473
474 }
475 sio << "}" << sio.nl;
476}
477
478void Context::setView( View *pNewView )
479{
480 delete pView;
481 pView = pNewView;
482}
483
484View *Context::getView()
485{
486 return pView;
487}
488
489Bu::Formatter &operator<<( Bu::Formatter &f, const Context &c )
490{
491 f << "Variables: " << c.sVars << f.nl;
492 f << "Targets: " << c.hTarget << f.nl;
493 f << "Rules: " << c.hRule << f.nl;
494
495 return f;
496}
497
498void Context::printBasicInfo()
499{
500 sio << "Available actions:" << sio.nl << "\t";
501 for( ActionHash::iterator i = hAction.begin(); i; i++ )
502 {
503 if( i != hAction.begin() )
504 sio << ", ";
505 sio << i.getKey();
506 }
507
508 TargetList lTargets = getExplicitTargets();
509 sio << sio.nl << sio.nl << "Available targets:" << sio.nl << "\t";
510 for( TargetList::iterator i = lTargets.begin(); i; i++ )
511 {
512 if( i != lTargets.begin() )
513 sio << ", ";
514 for( StrList::const_iterator j = (*i)->getOutputList().begin(); j; j++ )
515 {
516 if( j != (*i)->getOutputList().begin() )
517 sio << ", ";
518 sio << (*j);
519 }
520 }
521
522 sio << sio.nl << sio.nl;
523}
524
diff --git a/src/context.h b/src/context.h
new file mode 100644
index 0000000..0d9aaff
--- /dev/null
+++ b/src/context.h
@@ -0,0 +1,101 @@
1#ifndef CONTEXT_H
2#define CONTEXT_H
3
4#include "bu/hash.h"
5#include "bu/fstring.h"
6
7#include "variable.h"
8
9class Target;
10class Rule;
11class Function;
12class Action;
13class View;
14
15class Context
16{
17 friend Bu::Formatter &operator<<( Bu::Formatter &f, const Context &c );
18public:
19 Context();
20 virtual ~Context();
21
22 void addTarget( Target *pTarget );
23 void addRule( Rule *pRule );
24 void addFunction( Function *pFunction );
25 void addVariable( const Bu::FString &sName, const Variable &vValue );
26 void addAction( Action *pAction );
27 Action *getAction( const Bu::FString &sName );
28
29 void addTargetToTag( Target *pTarget, const Bu::FString &sTag );
30 void addTargetToTags( Target *pTarget, const StrList &sTags );
31 TargetList &getTag( const Bu::FString &sTag );
32
33 Variable &getVariable( const Bu::FString &sName );
34 void delVariable( const Bu::FString &sName );
35
36 void pushScope();
37 void pushScope( const VarHash &hNewVars );
38 VarHash &getScope();
39 void popScope();
40
41 Variable call( const Bu::FString &sName, Variable &input, VarList lParams );
42
43 Bu::FString expand( const Bu::FString &sIn );
44
45 Target *getTarget( const Bu::FString &sOutput );
46 TargetList getExplicitTargets();
47
48 /**
49 * This function actually builds the dependancy tree, and is responsible
50 * for creating all of the auto-generated targets that are required by the
51 * explicitly created targets.
52 */
53 void buildTargetTree( class Runner &r );
54
55 /**
56 * This is done as a final step, it goes through all targets and
57 * attaches things that they should have even if they haven't defined them,
58 * like a clean profile, they'll get that even if they haven't added one of
59 * their own. The defaults in this routine are added only if they aren't
60 * already defined in the target. It should be excetued after
61 * buildTargetTree, which means it doesn't need to affect rules.
62 */
63 void attachDefaults();
64
65 /**
66 * This function generates some default actions if they don't already
67 * exist, pretty straight forward, it will create all, clean, and default
68 * (default is the same as all).
69 */
70 void genDefaultActions();
71
72 void writeTargetDot();
73
74 void setView( View *pNewView );
75 View *getView();
76
77 void printBasicInfo();
78
79private:
80 void buildTargetTree( class Runner &r, class Target *pTarget, const Bu::FString &sInput, class Rule *pMaster, StrList &lNewIns );
81
82private:
83 typedef Bu::Hash<Bu::FString, Target *> TargetHash;
84 typedef Bu::Hash<Bu::FString, Rule *> RuleHash;
85 typedef Bu::Hash<Bu::FString, Function *> FunctionHash;
86 typedef Bu::Hash<Bu::FString, Action *> ActionHash;
87 typedef Bu::List<VarHash> ScopeStack;
88 typedef Bu::Hash<Bu::FString, TargetList> TagHash;
89
90 TargetHash hTarget;
91 RuleHash hRule;
92 FunctionHash hFunction;
93 ScopeStack sVars;
94 ActionHash hAction;
95 TagHash hTag;
96 View *pView;
97};
98
99Bu::Formatter &operator<<( Bu::Formatter &f, const Context &c );
100
101#endif
diff --git a/src/function.cpp b/src/function.cpp
new file mode 100644
index 0000000..f73f576
--- /dev/null
+++ b/src/function.cpp
@@ -0,0 +1,16 @@
1#include "function.h"
2
3Function::Function() :
4 pContext( NULL )
5{
6}
7
8Function::~Function()
9{
10}
11
12void Function::setContext( class Context *p )
13{
14 pContext = p;
15}
16
diff --git a/src/function.h b/src/function.h
new file mode 100644
index 0000000..9573bd3
--- /dev/null
+++ b/src/function.h
@@ -0,0 +1,23 @@
1#ifndef FUNCTION_H
2#define FUNCTION_H
3
4#include "bu/fstring.h"
5#include "variable.h"
6
7class Function
8{
9public:
10 Function();
11 virtual ~Function();
12
13 virtual Bu::FString getName() const=0;
14
15 virtual Variable call( Variable &input, VarList lParams )=0;
16
17 void setContext( class Context *p );
18
19protected:
20 class Context *pContext;
21};
22
23#endif
diff --git a/src/functionast.cpp b/src/functionast.cpp
new file mode 100644
index 0000000..0d9a938
--- /dev/null
+++ b/src/functionast.cpp
@@ -0,0 +1,33 @@
1#include "functionast.h"
2#include "astbranch.h"
3#include "astleaf.h"
4#include "runner.h"
5#include "context.h"
6
7FunctionAst::FunctionAst( const AstBranch *pRoot, class Runner *pRunner ) :
8 pRoot( pRoot ),
9 pRunner( pRunner )
10{
11 sName = dynamic_cast<AstLeaf *>(
12 *(*pRoot->getBranchBegin()).begin()
13 )->getStrValue();
14}
15
16FunctionAst::~FunctionAst()
17{
18}
19
20Bu::FString FunctionAst::getName() const
21{
22 return sName;
23}
24
25Variable FunctionAst::call( Variable &input, VarList /*lParams*/ )
26{
27 pContext->pushScope();
28 pContext->addVariable("INPUT", input );
29 Variable vRet = pRunner->run( (*(pRoot->getBranchBegin()+2)).begin() );
30 pContext->popScope();
31 return vRet;
32}
33
diff --git a/src/functionast.h b/src/functionast.h
new file mode 100644
index 0000000..b971683
--- /dev/null
+++ b/src/functionast.h
@@ -0,0 +1,21 @@
1#ifndef FUNCTION_AST_H
2#define FUNCTION_AST_H
3
4#include "function.h"
5
6class FunctionAst : public Function
7{
8public:
9 FunctionAst( const class AstBranch *pRoot, class Runner *pRunner );
10 virtual ~FunctionAst();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14
15private:
16 Bu::FString sName;
17 const class AstBranch *pRoot;
18 class Runner *pRunner;
19};
20
21#endif
diff --git a/src/functiondirname.cpp b/src/functiondirname.cpp
new file mode 100644
index 0000000..e8b728b
--- /dev/null
+++ b/src/functiondirname.cpp
@@ -0,0 +1,38 @@
1#include "functiondirname.h"
2
3FunctionDirName::FunctionDirName()
4{
5}
6
7FunctionDirName::~FunctionDirName()
8{
9}
10
11Bu::FString FunctionDirName::getName() const
12{
13 return "dirName";
14}
15
16Variable FunctionDirName::call( Variable &input, VarList /*lParams*/ )
17{
18 Bu::FString sFile;
19 sFile = input.getString();
20
21 Bu::FString::const_iterator i = sFile.begin();
22 Bu::FString::const_iterator io;
23 for(;;)
24 {
25 Bu::FString::const_iterator b = i.find('/');
26 if( !b )
27 {
28 return Variable( Bu::FString( sFile.begin(), io ) );
29 }
30 io = b;
31 i = b+1;
32 if( !i )
33 {
34 return Variable( Bu::FString( sFile.begin(), io ) );
35 }
36 }
37}
38
diff --git a/src/functiondirname.h b/src/functiondirname.h
new file mode 100644
index 0000000..830a992
--- /dev/null
+++ b/src/functiondirname.h
@@ -0,0 +1,17 @@
1#ifndef FUNCTION_DIR_NAME_H
2#define FUNCTION_DIR_NAME_H
3
4#include "function.h"
5
6class FunctionDirName : public Function
7{
8public:
9 FunctionDirName();
10 virtual ~FunctionDirName();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14
15};
16
17#endif
diff --git a/src/functiondirs.cpp b/src/functiondirs.cpp
new file mode 100644
index 0000000..fb64ef3
--- /dev/null
+++ b/src/functiondirs.cpp
@@ -0,0 +1,61 @@
1#include "functiondirs.h"
2
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <glob.h>
6#include <unistd.h>
7
8FunctionDirs::FunctionDirs()
9{
10}
11
12FunctionDirs::~FunctionDirs()
13{
14}
15
16Bu::FString FunctionDirs::getName() const
17{
18 return "dirs";
19}
20
21Variable FunctionDirs::call( Variable &/*input*/, VarList lParams )
22{
23 glob_t globbuf;
24
25 int flags = 0;
26
27 for( VarList::const_iterator i = lParams.begin(); i; i++ )
28 {
29 switch( (*i).getType() )
30 {
31 case Variable::typeString:
32 glob( (*i).getString().getStr(), flags, NULL, &globbuf );
33 flags |= GLOB_APPEND;
34 break;
35
36 case Variable::typeList:
37 throw Bu::ExceptionBase("Lists not supported in glob yet.");
38 break;
39
40 default:
41 throw Bu::ExceptionBase(
42 "Cannot use a non-string or non-list as a parameter to glob"
43 );
44 break;
45 }
46 }
47
48 Variable vRet( Variable::typeList );
49 struct stat s;
50 for( size_t j = 0; j < globbuf.gl_pathc; j++ )
51 {
52 stat( globbuf.gl_pathv[j], &s );
53 if( S_ISDIR( s.st_mode ) )
54 vRet.append( globbuf.gl_pathv[j] );
55 }
56
57 globfree( &globbuf );
58
59 return vRet;
60}
61
diff --git a/src/functiondirs.h b/src/functiondirs.h
new file mode 100644
index 0000000..5edfaf9
--- /dev/null
+++ b/src/functiondirs.h
@@ -0,0 +1,17 @@
1#ifndef FUNCTION_DIRS_H
2#define FUNCTION_DIRS_H
3
4#include "function.h"
5
6class FunctionDirs : public Function
7{
8public:
9 FunctionDirs();
10 virtual ~FunctionDirs();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14
15};
16
17#endif
diff --git a/src/functionexecute.cpp b/src/functionexecute.cpp
new file mode 100644
index 0000000..619d2c2
--- /dev/null
+++ b/src/functionexecute.cpp
@@ -0,0 +1,68 @@
1#include "functionexecute.h"
2#include "context.h"
3#include "view.h"
4
5#include <bu/sio.h>
6#include <bu/process.h>
7using namespace Bu;
8
9FunctionExecute::FunctionExecute()
10{
11}
12
13FunctionExecute::~FunctionExecute()
14{
15}
16
17Bu::FString FunctionExecute::getName() const
18{
19 return "execute";
20}
21
22Variable FunctionExecute::call( Variable &/*input*/, VarList lParams )
23{
24 // TODO This is lame, really lame, we need to exec on our own and process
25 // output appropriately.
26 pContext->getView()->cmdStarted( lParams.first().getString() );
27 Process pCmd( Process::Both, "/bin/bash", "/bin/bash", "-c",
28 lParams.first().getString().getStr(), NULL );
29 FString sStdOut, sStdErr;
30 while( pCmd.isRunning() )
31 {
32 char buf[4096];
33 bool out, err;
34 pCmd.select( out, err );
35 if( err )
36 {
37 int iRead = pCmd.readErr( buf, 4096 );
38 sStdErr.append( buf, iRead );
39 //sio << "Read " << iRead << " bytes of stderr." << sio.nl;
40 }
41 if( out )
42 {
43 int iRead = pCmd.read( buf, 4096 );
44 sStdOut.append( buf, iRead );
45 //sio << "Read " << iRead << " bytes of stdout." << sio.nl;
46 }
47 }
48
49 pContext->getView()->cmdFinished(
50 sStdOut, sStdErr, pCmd.childExitStatus()
51 );
52 if( pCmd.childExited() )
53 {
54 if( pCmd.childExitStatus() != 0 )
55 {
56 throw Bu::ExceptionBase("Command exited with errorcode %d.", pCmd.childExitStatus() );
57 }
58 }
59 else
60 {
61 pContext->getView()->cmdFinished(
62 sStdOut, sStdErr, pCmd.childExitStatus()
63 );
64 throw Bu::ExceptionBase("Command Failed");
65 }
66 return Variable( pCmd.childExitStatus() );
67}
68
diff --git a/src/functionexecute.h b/src/functionexecute.h
new file mode 100644
index 0000000..ebeaa7f
--- /dev/null
+++ b/src/functionexecute.h
@@ -0,0 +1,16 @@
1#ifndef FUNCTION_EXECUTE_H
2#define FUNCTION_EXECUTE_H
3
4#include "function.h"
5
6class FunctionExecute : public Function
7{
8public:
9 FunctionExecute();
10 virtual ~FunctionExecute();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14};
15
16#endif
diff --git a/src/functionexists.cpp b/src/functionexists.cpp
new file mode 100644
index 0000000..d2aa9e9
--- /dev/null
+++ b/src/functionexists.cpp
@@ -0,0 +1,34 @@
1#include "functionexists.h"
2
3#include <unistd.h>
4
5FunctionExists::FunctionExists()
6{
7}
8
9FunctionExists::~FunctionExists()
10{
11}
12
13Bu::FString FunctionExists::getName() const
14{
15 return "exists";
16}
17
18Variable FunctionExists::call( Variable &input, VarList lParams )
19{
20 Bu::FString sFile;
21 if( input.getType() != Variable::typeNone )
22 {
23 sFile = input.toString();
24 }
25 else
26 {
27 sFile = lParams.first().toString();
28 }
29 if( access( sFile.getStr(), F_OK ) == 0 )
30 return Variable( true );
31 else
32 return Variable( false );
33}
34
diff --git a/src/functionexists.h b/src/functionexists.h
new file mode 100644
index 0000000..8a3001a
--- /dev/null
+++ b/src/functionexists.h
@@ -0,0 +1,17 @@
1#ifndef FUNCTION_EXISTS_H
2#define FUNCTION_EXISTS_H
3
4#include "function.h"
5
6class FunctionExists : public Function
7{
8public:
9 FunctionExists();
10 virtual ~FunctionExists();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14
15};
16
17#endif
diff --git a/src/functionfilename.cpp b/src/functionfilename.cpp
new file mode 100644
index 0000000..21a4655
--- /dev/null
+++ b/src/functionfilename.cpp
@@ -0,0 +1,36 @@
1#include "functionfilename.h"
2
3FunctionFileName::FunctionFileName()
4{
5}
6
7FunctionFileName::~FunctionFileName()
8{
9}
10
11Bu::FString FunctionFileName::getName() const
12{
13 return "fileName";
14}
15
16Variable FunctionFileName::call( Variable &input, VarList /*lParams*/ )
17{
18 Bu::FString sFile;
19 sFile = input.getString();
20
21 Bu::FString::const_iterator i = sFile.begin();
22 for(;;)
23 {
24 Bu::FString::const_iterator b = i.find('/');
25 if( !b )
26 {
27 return Variable( Bu::FString( i ) );
28 }
29 i = b+1;
30 if( !i )
31 {
32 return Variable( Bu::FString( i ) );
33 }
34 }
35}
36
diff --git a/src/functionfilename.h b/src/functionfilename.h
new file mode 100644
index 0000000..1401fc7
--- /dev/null
+++ b/src/functionfilename.h
@@ -0,0 +1,17 @@
1#ifndef FUNCTION_FILE_NAME_H
2#define FUNCTION_FILE_NAME_H
3
4#include "function.h"
5
6class FunctionFileName : public Function
7{
8public:
9 FunctionFileName();
10 virtual ~FunctionFileName();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14
15};
16
17#endif
diff --git a/src/functionfiles.cpp b/src/functionfiles.cpp
new file mode 100644
index 0000000..e708d45
--- /dev/null
+++ b/src/functionfiles.cpp
@@ -0,0 +1,61 @@
1#include "functionfiles.h"
2
3#include <sys/types.h>
4#include <sys/stat.h>
5#include <glob.h>
6#include <unistd.h>
7
8FunctionFiles::FunctionFiles()
9{
10}
11
12FunctionFiles::~FunctionFiles()
13{
14}
15
16Bu::FString FunctionFiles::getName() const
17{
18 return "files";
19}
20
21Variable FunctionFiles::call( Variable &/*input*/, VarList lParams )
22{
23 glob_t globbuf;
24
25 int flags = 0;
26
27 for( VarList::const_iterator i = lParams.begin(); i; i++ )
28 {
29 switch( (*i).getType() )
30 {
31 case Variable::typeString:
32 glob( (*i).getString().getStr(), flags, NULL, &globbuf );
33 flags |= GLOB_APPEND;
34 break;
35
36 case Variable::typeList:
37 throw Bu::ExceptionBase("Lists not supported in glob yet.");
38 break;
39
40 default:
41 throw Bu::ExceptionBase(
42 "Cannot use a non-string or non-list as a parameter to glob"
43 );
44 break;
45 }
46 }
47
48 Variable vRet( Variable::typeList );
49 struct stat s;
50 for( size_t j = 0; j < globbuf.gl_pathc; j++ )
51 {
52 stat( globbuf.gl_pathv[j], &s );
53 if( S_ISREG( s.st_mode ) )
54 vRet.append( globbuf.gl_pathv[j] );
55 }
56
57 globfree( &globbuf );
58
59 return vRet;
60}
61
diff --git a/src/functionfiles.h b/src/functionfiles.h
new file mode 100644
index 0000000..711288a
--- /dev/null
+++ b/src/functionfiles.h
@@ -0,0 +1,17 @@
1#ifndef FUNCTION_FILES_H
2#define FUNCTION_FILES_H
3
4#include "function.h"
5
6class FunctionFiles : public Function
7{
8public:
9 FunctionFiles();
10 virtual ~FunctionFiles();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14
15};
16
17#endif
diff --git a/src/functiongetmakedeps.cpp b/src/functiongetmakedeps.cpp
new file mode 100644
index 0000000..1aded15
--- /dev/null
+++ b/src/functiongetmakedeps.cpp
@@ -0,0 +1,59 @@
1#include "functiongetmakedeps.h"
2
3#include <bu/process.h>
4#include <bu/sio.h>
5using namespace Bu;
6
7FunctionGetMakeDeps::FunctionGetMakeDeps()
8{
9}
10
11FunctionGetMakeDeps::~FunctionGetMakeDeps()
12{
13}
14
15Bu::FString FunctionGetMakeDeps::getName() const
16{
17 return "getMakeDeps";
18}
19
20Variable FunctionGetMakeDeps::call( Variable &/*input*/, VarList lParams )
21{
22 Process p( Process::StdOut, "/bin/bash", "/bin/bash", "-c",
23 lParams.first().getString().getStr(), NULL );
24
25 // Gather all data from the command.
26 Bu::FString sBuf;
27 while( !p.isEos() )
28 {
29 char buf[4096];
30 int iRead = p.read( buf, 4096 );
31 sBuf.append( buf, iRead );
32 }
33
34 Variable vRet( Variable::typeList );
35
36 Bu::FString::iterator i, j;
37 i = sBuf.find(':')+2;
38 while( i )
39 {
40 // Find whitespace at the end of the word, this one is easy, there's
41 // always a space after a word
42 for( j = i; j && *j != ' ' && *j != '\n' && *j != '\r'; j++ ) { }
43
44 Bu::FString sTmp( i, j );
45 vRet.append( sTmp );
46
47 // Find the begining of the next word, trickier, we don't want to go
48 // off the end, and we need to skip \ chars at the ends of lines, right
49 // now this is too stupid to do that, so it may not work on windows.
50 // TODO: perhaps make this only skip \ chars at the ends of lines,
51 // we'll see if it matters.
52 for( i = j+1;
53 i && (*i == ' ' || *i == '\\' || *i == '\n' || *i == '\r'); i++ )
54 { }
55 }
56
57 return vRet;
58}
59
diff --git a/src/functiongetmakedeps.h b/src/functiongetmakedeps.h
new file mode 100644
index 0000000..b8f20d5
--- /dev/null
+++ b/src/functiongetmakedeps.h
@@ -0,0 +1,16 @@
1#ifndef FUNCTION_GET_MAKE_DEPS_H
2#define FUNCTION_GET_MAKE_DEPS_H
3
4#include "function.h"
5
6class FunctionGetMakeDeps : public Function
7{
8public:
9 FunctionGetMakeDeps();
10 virtual ~FunctionGetMakeDeps();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14};
15
16#endif
diff --git a/src/functionmatches.cpp b/src/functionmatches.cpp
new file mode 100644
index 0000000..4e4b7ff
--- /dev/null
+++ b/src/functionmatches.cpp
@@ -0,0 +1,141 @@
1#include "functionmatches.h"
2
3#include <unistd.h>
4
5FunctionMatches::FunctionMatches()
6{
7}
8
9FunctionMatches::~FunctionMatches()
10{
11}
12
13Bu::FString FunctionMatches::getName() const
14{
15 return "matches";
16}
17
18bool FunctionMatches::globcmp( const Bu::FString &sTxt, const Bu::FString &sMatches )
19{
20 Bu::FString::const_iterator t, g;
21 t = sTxt.begin();
22 g = sMatches.begin();
23
24 while( g && t )
25 {
26 switch( *g )
27 {
28 case '*':
29 // First, if the * is at the end, then we do match, it doesn't
30 // matter what is in sTxt
31 if( !(g+1) )
32 return true;
33 // Now attempt to scan for the remainder as a matched set
34 {
35 Bu::FString::const_iterator tn = t+1, gn = g+1, gi=g+1;
36 bool bFoundMatch = false;
37 while( tn && gn )
38 {
39 if( *gn == '*' )
40 {
41 g = gn;
42 t = tn;
43 break;
44 }
45 if( *tn == *gn )
46 {
47 g = gn;
48 t = tn;
49 tn++;
50 gn++;
51 bFoundMatch = true;
52 }
53 else
54 {
55 gn = gi;
56 tn++;
57 bFoundMatch = false;
58 }
59 }
60 if( bFoundMatch == false )
61 return false;
62 if( !tn && !gn && bFoundMatch )
63 return true;
64 }
65 break;
66
67 case '?':
68 // Don't bother checking.
69 t++;
70 g++;
71 break;
72
73 default:
74 if( *t != *g )
75 return false;
76 t++;
77 g++;
78 break;
79 }
80 }
81 if( t || (g && *g != '*') )
82 return false;
83 return true;
84}
85
86bool FunctionMatches::matchlist( const Bu::FString &sTxt, VarList &lParams )
87{
88 for( VarList::iterator i = lParams.begin(); i; i++ )
89 {
90 if( (*i).getType() == Variable::typeList )
91 {
92 for( VarList::iterator j = (*i).begin(); j; j++ )
93 {
94 if( globcmp( sTxt, (*j).toString() ) )
95 return true;
96 }
97 }
98 else
99 {
100 if( globcmp( sTxt, (*i).toString() ) )
101 return true;
102 }
103 }
104 return false;
105}
106
107Variable FunctionMatches::call( Variable &input, VarList lParams )
108{
109 switch( input.getType() )
110 {
111 case Variable::typeString:
112 {
113 Bu::FString sTxt = input.getString();
114 return Variable( matchlist( sTxt, lParams ) );
115 }
116 break;
117
118 case Variable::typeList:
119 {
120 Variable vRet( Variable::typeList );
121 for( VarList::iterator i = input.begin(); i; i++ )
122 {
123 if( (*i).getType() != Variable::typeString )
124 continue;
125 Bu::FString sTxt = (*i).getString();
126 if( matchlist( sTxt, lParams ) )
127 vRet.append( *i );
128 }
129 return vRet;
130 }
131 break;
132
133 default:
134 throw Bu::ExceptionBase("You can only use a string or list as the "
135 "input to matches.");
136 break;
137 }
138
139 return Variable();
140}
141
diff --git a/src/functionmatches.h b/src/functionmatches.h
new file mode 100644
index 0000000..7757a44
--- /dev/null
+++ b/src/functionmatches.h
@@ -0,0 +1,23 @@
1#ifndef FUNCTION_MATCHES_H
2#define FUNCTION_MATCHES_H
3
4#include "function.h"
5
6class FunctionMatches : public Function
7{
8public:
9 FunctionMatches();
10 virtual ~FunctionMatches();
11
12 /**
13 * Really basic globbing function, it doesn't handle character classes,
14 * just * and ?. We can expand on it later, it may be handy.
15 */
16 bool globcmp( const Bu::FString &sTxt, const Bu::FString &sMatches );
17 bool matchlist( const Bu::FString &sTxt, VarList &lParams );
18 virtual Bu::FString getName() const;
19 virtual Variable call( Variable &input, VarList lParams );
20
21};
22
23#endif
diff --git a/src/functionreplace.cpp b/src/functionreplace.cpp
new file mode 100644
index 0000000..d269083
--- /dev/null
+++ b/src/functionreplace.cpp
@@ -0,0 +1,47 @@
1#include "functionreplace.h"
2
3FunctionReplace::FunctionReplace()
4{
5}
6
7FunctionReplace::~FunctionReplace()
8{
9}
10
11Bu::FString FunctionReplace::getName() const
12{
13 return "replace";
14}
15
16Variable FunctionReplace::call( Variable &input, VarList lParams )
17{
18 Bu::FString sA, sB;
19 sA = lParams.first().getString();
20 sB = lParams.last().getString();
21 switch( input.getType() )
22 {
23 case Variable::typeString:
24 {
25 Variable vOut( input.getString().replace( sA, sB ) );
26 return vOut;
27 }
28 break;
29
30 case Variable::typeList:
31 {
32 Variable vOut( Variable::typeList );
33 for( VarList::iterator i = input.begin(); i; i++ )
34 {
35 vOut.append( (*i).getString().replace( sA, sB ) );
36 }
37 return vOut;
38 }
39 break;
40
41 default:
42 break;
43 }
44 throw Bu::ExceptionBase(
45 "replace does not work on non-string or non-list types.");
46}
47
diff --git a/src/functionreplace.h b/src/functionreplace.h
new file mode 100644
index 0000000..1bf4dae
--- /dev/null
+++ b/src/functionreplace.h
@@ -0,0 +1,16 @@
1#ifndef FUNCTION_REPLACE_H
2#define FUNCTION_REPLACE_H
3
4#include "function.h"
5
6class FunctionReplace : public Function
7{
8public:
9 FunctionReplace();
10 virtual ~FunctionReplace();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14};
15
16#endif
diff --git a/src/functiontargets.cpp b/src/functiontargets.cpp
new file mode 100644
index 0000000..93fbb96
--- /dev/null
+++ b/src/functiontargets.cpp
@@ -0,0 +1,39 @@
1#include "functiontargets.h"
2#include "context.h"
3#include "target.h"
4
5FunctionTargets::FunctionTargets()
6{
7}
8
9FunctionTargets::~FunctionTargets()
10{
11}
12
13Bu::FString FunctionTargets::getName() const
14{
15 return "targets";
16}
17
18Variable FunctionTargets::call( Variable &/*input*/, VarList lParams )
19{
20 Variable vRet( Variable::typeList );
21 TargetList lTrg;
22 if( lParams.getSize() == 0 )
23 {
24 lTrg = pContext->getExplicitTargets();
25 }
26 else
27 {
28 lTrg = pContext->getTag( lParams.first().toString() );
29 }
30 for( TargetList::const_iterator i = lTrg.begin(); i; i++ )
31 {
32 for( StrList::const_iterator j = (*i)->getOutputList().begin(); j; j++ )
33 {
34 vRet.append( *j );
35 }
36 }
37 return vRet;
38}
39
diff --git a/src/functiontargets.h b/src/functiontargets.h
new file mode 100644
index 0000000..9b65d30
--- /dev/null
+++ b/src/functiontargets.h
@@ -0,0 +1,16 @@
1#ifndef FUNCTION_TARGETS_H
2#define FUNCTION_TARGETS_H
3
4#include "function.h"
5
6class FunctionTargets : public Function
7{
8public:
9 FunctionTargets();
10 virtual ~FunctionTargets();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14};
15
16#endif
diff --git a/src/functiontostring.cpp b/src/functiontostring.cpp
new file mode 100644
index 0000000..0c04091
--- /dev/null
+++ b/src/functiontostring.cpp
@@ -0,0 +1,50 @@
1#include "functiontostring.h"
2
3#include <stdlib.h>
4#include <bu/sio.h>
5using namespace Bu;
6
7FunctionToString::FunctionToString()
8{
9}
10
11FunctionToString::~FunctionToString()
12{
13}
14
15Bu::FString FunctionToString::getName() const
16{
17 return "toString";
18}
19
20Variable FunctionToString::call( Variable &input, VarList lParams )
21{
22 Bu::FString sStr;
23 Bu::FString sSep;
24 if( lParams.getSize() == 0 )
25 {
26 sSep = " ";
27 }
28 else
29 {
30 sSep = lParams.first().getString();
31 }
32 switch( input.getType() )
33 {
34 case Variable::typeString:
35 return input;
36
37 case Variable::typeList:
38 for( VarList::iterator i = input.begin(); i; i++ )
39 {
40 if( i != input.begin() )
41 sStr += sSep;
42 sStr += (*i).getString();
43 }
44 return Variable( sStr );
45
46 default:
47 return Variable( input.getString() );
48 }
49}
50
diff --git a/src/functiontostring.h b/src/functiontostring.h
new file mode 100644
index 0000000..3c3ecc7
--- /dev/null
+++ b/src/functiontostring.h
@@ -0,0 +1,16 @@
1#ifndef FUNCTION_TO_STRING_H
2#define FUNCTION_TO_STRING_H
3
4#include "function.h"
5
6class FunctionToString : public Function
7{
8public:
9 FunctionToString();
10 virtual ~FunctionToString();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14};
15
16#endif
diff --git a/src/functionunlink.cpp b/src/functionunlink.cpp
new file mode 100644
index 0000000..addcfd9
--- /dev/null
+++ b/src/functionunlink.cpp
@@ -0,0 +1,51 @@
1#include "functionunlink.h"
2
3#include <unistd.h>
4#include <stdlib.h>
5#include <bu/sio.h>
6using namespace Bu;
7
8FunctionUnlink::FunctionUnlink()
9{
10}
11
12FunctionUnlink::~FunctionUnlink()
13{
14}
15
16Bu::FString FunctionUnlink::getName() const
17{
18 return "unlink";
19}
20
21Variable FunctionUnlink::call( Variable &/*input*/, VarList lParams )
22{
23 //sio << "Unlink called: " << lParams << sio.nl;
24 for( VarList::iterator p = lParams.begin(); p; p++ )
25 {
26 switch( (*p).getType() )
27 {
28 case Variable::typeString:
29 //sio << " xx> " << (*p).getString() << sio.nl;
30 unlink( (*p).getString().getStr() );
31 break;
32
33 case Variable::typeList:
34 //sio << " xx>";
35 for( VarList::iterator i = (*p).begin(); i; i++ )
36 {
37 //sio << " " << (*i).getString();
38 unlink( (*i).getString().getStr() );
39 }
40 //sio << sio.nl;
41 break;
42
43 default:
44 throw Bu::ExceptionBase("Hey, wrong type passed.");
45 break;
46 }
47 }
48
49 return Variable();
50}
51
diff --git a/src/functionunlink.h b/src/functionunlink.h
new file mode 100644
index 0000000..ac3f21e
--- /dev/null
+++ b/src/functionunlink.h
@@ -0,0 +1,16 @@
1#ifndef FUNCTION_UNLINK_H
2#define FUNCTION_UNLINK_H
3
4#include "function.h"
5
6class FunctionUnlink : public Function
7{
8public:
9 FunctionUnlink();
10 virtual ~FunctionUnlink();
11
12 virtual Bu::FString getName() const;
13 virtual Variable call( Variable &input, VarList lParams );
14};
15
16#endif
diff --git a/src/main.cpp b/src/main.cpp
new file mode 100644
index 0000000..142927d
--- /dev/null
+++ b/src/main.cpp
@@ -0,0 +1,255 @@
1#include "buildparser.h"
2#include "context.h"
3#include "ast.h"
4#include "runner.h"
5#include "target.h"
6
7#include "viewplugger.h"
8
9#include <bu/optparser.h>
10#include <bu/sio.h>
11#include <sys/types.h>
12#include <dirent.h>
13#include <stdlib.h>
14#include <unistd.h>
15
16extern char **environ;
17
18using namespace Bu;
19
20class Options : public Bu::OptParser
21{
22public:
23 Options( int argc, char *argv[] ) :
24 sView("default"),
25 sAction("default"),
26 sConfig("default.bld"),
27 bDot( false ),
28 bDebug( false ),
29 bAutoInclude( true ),
30 bAstDump( false ),
31 bEnviron( true ),
32 iInfoLevel( 0 )
33 {
34 bool bClean = false;
35 addHelpBanner("build mark 3\n");
36
37 Bu::FString sViews("Select a view from: ");
38 StrList lViews = ViewPlugger::getInstance().getPluginList();
39 for( StrList::iterator i = lViews.begin(); i; i++ )
40 {
41 if( i != lViews.begin() )
42 sViews += ", ";
43 sViews += *i;
44 }
45
46 addHelpBanner("The following options do things other than build:");
47 addOption( iInfoLevel, 'i', "info", "Display some basic info about the "
48 "loaded build config, including available targets.");
49
50 addHelpBanner("The following options control general execution:");
51 addOption( sView, 'v', "view", sViews );
52 addOption( sConfig, 'f', "file", "Select a different config file." );
53 addOption( bClean, 'c', "Shorthand for running action 'clean'. If an "
54 "action is specified, this will modify it to run 'clean-action'.");
55 addOption( slot(this, &Options::onChdir), 'C', "chdir",
56 "Change to directory before doing anything else.");
57
58 addHelpBanner("\nThe following options control debugging:");
59 addOption( bEnviron, "no-env", "Do not import environment variables.");
60 addOption( bDot, "dot", "Generate a dot chart after execution." );
61 addOption( bDebug, "debug",
62 "Dump massive amounts of hard to read debugging data." );
63 addOption( bAstDump, "debug-ast",
64 "Display the raw AST that is computed from parsing the input. "
65 "You should probably never ever use this, it'll scare you."
66 );
67
68 setOverride( "no-env", "false" );
69 setOverride( "dot", "true" );
70 setOverride( "debug", "true" );
71 setOverride( "debug-ast", "true" );
72 setOverride( "info", "1" );
73 setOverride( 'c', "true" );
74
75 addHelpOption();
76
77 setNonOption( slot( this, &Options::onNonOption ) );
78
79 parse( argc, argv );
80
81 if( bClean )
82 {
83 if( sAction == "default" )
84 sAction = "clean";
85 else
86 sAction.prepend("clean-");
87 }
88 }
89
90 virtual ~Options()
91 {
92 }
93
94 int onChdir( StrArray sParams )
95 {
96 if( sParams.getSize() == 0 )
97 {
98 sio << "You must specify a directory name!" << sio.nl << sio.nl;
99 exit(2);
100 }
101 chdir( sParams[1].getStr() );
102 return 1;
103 }
104
105 int onNonOption( StrArray sParams )
106 {
107 sAction = sParams[0];
108 return 0;
109 }
110
111 Bu::FString sView;
112 Bu::FString sAction;
113 Bu::FString sConfig;
114 bool bDot;
115 bool bDebug;
116 bool bAutoInclude;
117 bool bAstDump;
118 bool bEnviron;
119 int iInfoLevel;
120};
121
122int main( int argc, char *argv[] )
123{
124 typedef Bu::List<Bu::FString> StrList;
125 StrList lShareList;
126 lShareList.append("/usr/share/build/").append("./share/");
127 Ast ast;
128 Context cnt;
129 BuildParser bp( ast );
130
131 for( StrList::iterator i = lShareList.begin(); i; i++ )
132 {
133 bp.addIncludePath( *i + "include");
134 }
135
136 Options opts( argc, argv );
137
138 try
139 {
140 cnt.setView( ViewPlugger::getInstance().instantiate( opts.sView ) );
141 }
142 catch( Bu::HashException &e )
143 {
144 sio << "Error: Invalid view specified, please choose from the "
145 "following choices:" << sio.nl << sio.nl << "\t";
146
147 StrList lViews = ViewPlugger::getInstance().getPluginList();
148 for( StrList::iterator i = lViews.begin(); i; i++ )
149 {
150 if( i != lViews.begin() )
151 sio << ", ";
152 sio << *i;
153 }
154 sio << sio.nl << sio.nl;
155 return 1;
156 }
157
158 // Load up the environment as vars.
159 if( opts.bEnviron )
160 {
161 for( char **env = environ; *env; env++ )
162 {
163 int iSplit;
164 for( iSplit = 0; (*env)[iSplit] != '='; iSplit++ ) { }
165 cnt.addVariable(
166 FString( *env, iSplit ),
167 FString( *env+iSplit+1 )
168 );
169 }
170 }
171
172 if( opts.bAutoInclude )
173 {
174 DIR *d;
175 Bu::FString sAutoDir;
176 for( StrList::iterator i = lShareList.begin(); i; i++ )
177 {
178 sAutoDir = *i + "autoinclude";
179 d = opendir( sAutoDir.getStr() );
180 if( d )
181 break;
182 }
183 if( !d )
184 {
185 cnt.getView()->sysWarning(
186 "Could not find an autoinclude directory."
187 );
188 }
189 else
190 {
191 struct dirent *de;
192 while( (de = readdir( d )) )
193 {
194 if( de->d_name[0] == '.' || (de->d_type != DT_REG) )
195 continue;
196 //sio << "Auto-including: " << de->d_name << sio.nl;
197 bp.load( sAutoDir + "/" + de->d_name );
198 }
199 }
200 }
201
202 bp.load( opts.sConfig );
203
204 if( opts.bAstDump )
205 {
206 sio << ast << sio.nl << sio.nl;
207 return 0;
208 }
209
210// sio << ast << sio.nl;
211
212 Runner r( ast, cnt );
213 r.initialize();
214
215 r.run();
216
217 switch( opts.iInfoLevel )
218 {
219 case 0:
220 // Do nothing
221 break;
222
223 case 1:
224 cnt.printBasicInfo();
225 return 0;
226 }
227
228 try
229 {
230 r.execAction( opts.sAction );
231 }
232 catch( std::exception &e )
233 {
234 cnt.getView()->sysError(e.what());
235 }
236 catch( ... )
237 {
238 cnt.getView()->sysError(
239 "Unknown error occured, this is probably bad..."
240 );
241 }
242
243 if( opts.bDot )
244 {
245 cnt.writeTargetDot();
246 }
247
248 if( opts.bDebug )
249 {
250 sio << "Final context:" << sio.nl << cnt << sio.nl << sio.nl;
251 }
252
253 return 0;
254}
255
diff --git a/src/profile.cpp b/src/profile.cpp
new file mode 100644
index 0000000..878a6e9
--- /dev/null
+++ b/src/profile.cpp
@@ -0,0 +1,116 @@
1#include "profile.h"
2#include "ast.h"
3#include "astbranch.h"
4#include "astleaf.h"
5#include "condition.h"
6
7#include "conditionfiletime.h"
8#include "conditionalways.h"
9#include "conditionnever.h"
10
11#include <bu/sio.h>
12using namespace Bu;
13
14Profile::Profile( const class AstBranch *pRoot ) :
15 pRoot( pRoot ),
16 pCond( NULL ),
17 pAst( NULL )
18{
19 sName = dynamic_cast<const AstLeaf *>(
20 (*pRoot->getBranchBegin()).first()
21 )->getStrValue();
22
23 setCondition();
24}
25
26Profile::Profile( const Profile &rSrc ) :
27 sName( rSrc.sName ),
28 pRoot( rSrc.pRoot ),
29 pCond( rSrc.pCond->clone() ),
30 pAst( NULL )
31{
32}
33
34Profile::~Profile()
35{
36 delete pAst;
37 pAst = NULL;
38}
39
40const Bu::FString &Profile::getName() const
41{
42 return sName;
43}
44
45const AstBranch *Profile::getRoot() const
46{
47 return pRoot;
48}
49
50const Condition *Profile::getCond() const
51{
52 return pCond;
53}
54
55bool Profile::shouldExec( class Runner &r, class Target &rTarget ) const
56{
57 return pCond->shouldExec( r, rTarget );
58}
59
60Profile *Profile::genDefaultClean()
61{
62 Ast *pAst = new Ast();
63 pAst->addNode( AstNode::typeProfile );
64 pAst->openBranch();
65 pAst->addNode( AstNode::typeString, "clean" );
66 pAst->openBranch();
67 pAst->addNode( AstNode::typeCondition, "always" );
68 pAst->addNode( AstNode::typeFunction );
69 pAst->openBranch();
70 pAst->addNode( AstNode::typeString, "unlink" );
71 pAst->openBranch();
72 pAst->addNode( AstNode::typeVariable, "OUTPUT" );
73 pAst->closeNode();
74 pAst->closeNode();
75 //pAst->closeNode();
76 Profile *pRet = new Profile(
77 dynamic_cast<const AstBranch *>(*pAst->getNodeBegin())
78 );
79 pRet->pAst = pAst;
80
81 return pRet;
82}
83
84void Profile::setCondition()
85{
86 for( AstBranch::NodeList::const_iterator i =
87 (*(pRoot->getBranchBegin()+1)).begin(); i; i++ )
88 {
89 if( (*i)->getType() == AstNode::typeCondition )
90 {
91 Bu::FString sCond = dynamic_cast<const AstLeaf *>(*i)->getStrValue();
92 if( sCond == "filetime" )
93 {
94 delete pCond;
95 pCond = new ConditionFileTime();
96 }
97 else if( sCond == "always" )
98 {
99 delete pCond;
100 pCond = new ConditionAlways();
101 }
102 else if( sCond == "never" )
103 {
104 delete pCond;
105 pCond = new ConditionNever();
106 }
107 }
108 }
109
110 if( pCond == NULL )
111 {
112 // The default condition
113 pCond = new ConditionFileTime();
114 }
115}
116
diff --git a/src/profile.h b/src/profile.h
new file mode 100644
index 0000000..dbcc1ea
--- /dev/null
+++ b/src/profile.h
@@ -0,0 +1,30 @@
1#ifndef PROFILE_H
2#define PROFILE_H
3
4#include <bu/fstring.h>
5
6class Profile
7{
8public:
9 Profile( const class AstBranch *pRoot );
10 Profile( const Profile &rSrc );
11 virtual ~Profile();
12
13 const Bu::FString &getName() const;
14 const class AstBranch *getRoot() const;
15 const class Condition *getCond() const;
16 bool shouldExec( class Runner &r, class Target &rTarget ) const;
17
18 static Profile *genDefaultClean();
19
20private:
21 void setCondition();
22
23private:
24 Bu::FString sName;
25 const class AstBranch *pRoot;
26 class Condition *pCond;
27 class Ast *pAst;
28};
29
30#endif
diff --git a/src/rule.cpp b/src/rule.cpp
new file mode 100644
index 0000000..4c42346
--- /dev/null
+++ b/src/rule.cpp
@@ -0,0 +1,167 @@
1#include "rule.h"
2#include "target.h"
3#include "astbranch.h"
4#include "astleaf.h"
5#include "runner.h"
6#include "variable.h"
7#include "context.h"
8#include "condition.h"
9#include "profile.h"
10
11#include <bu/sio.h>
12using namespace Bu;
13
14Rule::Rule( const Bu::FString &sName ) :
15 sName( sName ),
16 pInput( NULL )
17{
18}
19
20Rule::~Rule()
21{
22}
23
24const Bu::FString &Rule::getName() const
25{
26 return sName;
27}
28
29void Rule::setInput( const AstBranch *pNewInput )
30{
31 pInput = pNewInput;
32}
33
34const AstBranch *Rule::getInput() const
35{
36 return pInput;
37}
38
39bool Rule::hasOutputs() const
40{
41 return !lOutput.isEmpty();
42}
43
44void Rule::addOutput( const AstBranch *pNewOutput )
45{
46 lOutput.append( pNewOutput );
47}
48
49void Rule::addProfile( const AstBranch *pProfRoot )
50{
51 Profile *pProf = new Profile( pProfRoot );
52 hProfiles.insert( pProf->getName(), pProf );
53 /*
54 hProfiles.insert(
55 dynamic_cast<const AstLeaf *>(
56 (*pProfile->getBranchBegin()).first()
57 )->getStrValue(),
58 pProfile
59 );
60 */
61}
62
63void Rule::prepTarget( class Target *pTarget )
64{
65 pTarget->setDisplay( getDisplay() );
66 for( ProfileHash::iterator i = hProfiles.begin(); i; i++ )
67 {
68 pTarget->addProfile( *i );
69 }
70 for( AstBranchList::iterator i = lRequires.begin(); i; i++ )
71 {
72 pTarget->addRequires( *i );
73 }
74}
75
76Target *Rule::createTarget( class Runner &r, const Bu::FString &sInput )
77{
78 r.getContext().pushScope();
79 r.getContext().addVariable("INPUT", sInput );
80 Target *pTrg = new Target( false );
81 for( AstBranchList::iterator i = lOutput.begin(); i; i++ )
82 {
83 Variable vOut = r.execExpr(
84 (*(*i)->getBranchBegin()).begin(),
85 Variable( sInput )
86 );
87 if( vOut.getType() == Variable::typeString )
88 {
89 pTrg->addOutput( vOut.getString() );
90 }
91 else if( vOut.getType() == Variable::typeList )
92 {
93 for( VarList::iterator j = vOut.begin(); j; j++ )
94 {
95 pTrg->addOutput( (*j).getString() );
96 }
97 }
98 }
99 r.getContext().addVariable("OUTPUT", pTrg->getOutputList() );
100 pTrg->addInput( sInput );
101 pTrg->setDisplay( getDisplay() );
102 for( ProfileHash::iterator i = hProfiles.begin(); i; i++ )
103 {
104 pTrg->addProfile( *i );
105 }
106 for( AstBranchList::iterator i = lRequires.begin(); i; i++ )
107 {
108 pTrg->addRequires( *i );
109 }
110 pTrg->setVars( r.getContext().getScope() );
111 r.getContext().popScope();
112
113 return pTrg;
114}
115
116bool Rule::ruleMatches( Runner &r, const Bu::FString &sInput )
117{
118 r.getContext().pushScope();
119 r.getContext().addVariable("INPUT", sInput );
120 Variable vInput( sInput );
121 Variable vOut = r.execExpr(
122 (*pInput->getBranchBegin()).begin(),
123 vInput
124 );
125 r.getContext().popScope();
126 if( vOut.getType() == Variable::typeBool )
127 return vOut.getBool();
128 else if( vOut.getType() == Variable::typeList )
129 return vOut.begin();
130 return false;
131}
132
133void Rule::addTag( const Bu::FString &sTag )
134{
135 lsTags.append( sTag );
136}
137
138const StrList &Rule::getTagList() const
139{
140 return lsTags;
141}
142
143void Rule::setDisplay( const Bu::FString &sStr )
144{
145 sDisplay = sStr;
146}
147
148const Bu::FString &Rule::getDisplay() const
149{
150 return ((bool)sDisplay)?(sDisplay):(sName);
151}
152
153void Rule::addRequires( const AstBranch *pBr )
154{
155 lRequires.append( pBr );
156}
157
158Bu::Formatter &operator<<( Bu::Formatter &f, const Rule &/*t*/ )
159{
160 return f << "rule";
161}
162
163template<> Bu::Formatter &Bu::operator<< <Rule>( Bu::Formatter &f, const Rule *t )
164{
165 return f << (*t);
166}
167
diff --git a/src/rule.h b/src/rule.h
new file mode 100644
index 0000000..a3c9344
--- /dev/null
+++ b/src/rule.h
@@ -0,0 +1,53 @@
1#ifndef RULE_H
2#define RULE_H
3
4#include "types.h"
5#include <bu/formatter.h>
6
7class Rule
8{
9 friend Bu::Formatter &operator<<( Bu::Formatter &f, const Rule &t );
10public:
11 Rule( const Bu::FString &sName );
12 virtual ~Rule();
13
14 const Bu::FString &getName() const;
15
16 void setInput( const AstBranch *pNewInput );
17 const AstBranch *getInput() const;
18
19 bool hasOutputs() const;
20
21 void addOutput( const AstBranch *pNewOutput );
22 void addProfile( const AstBranch *pProfile );
23
24 void prepTarget( class Target *pTarget );
25 class Target *createTarget( class Runner &r, const Bu::FString &sInput );
26 bool ruleMatches( class Runner &r, const Bu::FString &sInput );
27
28 void addTag( const Bu::FString &sTag );
29 const StrList &getTagList() const;
30
31 void setDisplay( const Bu::FString &sStr );
32 const Bu::FString &getDisplay() const;
33
34 void addRequires( const AstBranch *pBr );
35
36private:
37 Bu::FString sName;
38 Bu::FString sDisplay;
39 const AstBranch *pInput;
40 AstBranchList lOutput;
41 ProfileHash hProfiles;
42 StrList lsTags;
43 AstBranchList lRequires;
44};
45
46Bu::Formatter &operator<<( Bu::Formatter &f, const Rule &t );
47
48namespace Bu
49{
50 template<> Bu::Formatter &operator<< <Rule>( Bu::Formatter &f, const Rule *t );
51};
52
53#endif
diff --git a/src/runner.cpp b/src/runner.cpp
new file mode 100644
index 0000000..ace9ce9
--- /dev/null
+++ b/src/runner.cpp
@@ -0,0 +1,922 @@
1#include "runner.h"
2
3#include "ast.h"
4#include "astnode.h"
5#include "astleaf.h"
6#include "astbranch.h"
7#include "context.h"
8#include "functionast.h"
9#include "rule.h"
10#include "variable.h"
11#include "target.h"
12#include "action.h"
13#include "profile.h"
14#include "view.h"
15
16#include "bu/sio.h"
17using Bu::sio;
18
19Runner::Runner( Ast &rAst, Context &rCont ) :
20 rAst( rAst ),
21 rCont( rCont ),
22 pCurTarget( NULL ),
23 pCurRule( NULL )
24{
25}
26
27Runner::~Runner()
28{
29}
30
31void Runner::initialize()
32{
33 for( Ast::NodeList::const_iterator i = rAst.getNodeBegin(); i; i++ )
34 {
35 if( (*i)->getType() == AstNode::typeFunctionDef )
36 {
37 AstBranch *pFnc = dynamic_cast<AstBranch *>(*i);
38 rCont.addFunction( new FunctionAst( pFnc, this ) );
39 }
40 else if( (*i)->getType() == AstNode::typeActionDef )
41 {
42 AstBranch *pAction = dynamic_cast<AstBranch *>(*i);
43 rCont.addAction( new Action( pAction ) );
44 }
45 }
46}
47
48Variable Runner::execFunc( const AstBranch *pFunc, Variable &vIn )
49{
50 Bu::FString sName = dynamic_cast<const AstLeaf *>(
51 (*pFunc->getBranchBegin()).first())->getStrValue();
52
53 VarList lParams;
54 for( AstBranch::BranchList::const_iterator p =
55 pFunc->getBranchBegin()+1; p; p++ )
56 {
57 lParams.append( execExpr( (*p).begin() ) );
58 }
59
60 return rCont.call( sName, vIn, lParams );
61}
62
63Variable Runner::execExpr( AstBranch::NodeList::const_iterator e )
64{
65 Variable vBlank;
66 return execExpr( e, vBlank );
67}
68
69Variable Runner::execExpr( AstBranch::NodeList::const_iterator e,
70 const Variable &vIn )
71{
72// Variable v( vIn );
73 VarList lStack;
74 lStack.push( vIn );
75
76 for(; e; e++ )
77 {
78 if( ((*e)->getType()&AstNode::typeClassMask) == AstNode::typeBranch )
79 {
80 const AstBranch *pBranch = dynamic_cast<const AstBranch *>( *e );
81 switch( pBranch->getType() )
82 {
83 case AstNode::typeFunction:
84 //sio << "FUNC: " << *pBranch << sio.nl << sio.nl;
85 {
86 Variable v = lStack.peekPop();
87 lStack.push( execFunc( pBranch, v ) );
88 }
89 break;
90
91 case AstNode::typeSet:
92 lStack.push( doSet( pBranch ) );
93 break;
94
95 case AstNode::typeList:
96 {
97 Variable vLst( Variable::typeList );
98 for( AstBranch::BranchList::const_iterator i =
99 pBranch->getBranchBegin(); i; i++ )
100 {
101 vLst.append( execExpr( (*i).begin() ) );
102 }
103 lStack.push( vLst );
104 }
105 break;
106
107 case AstNode::typeExpr:
108 {
109 sio << "!!! typeExpr in an expr maybe should be an error..." << sio.nl;
110 for( AstBranch::BranchList::const_iterator i =
111 pBranch->getBranchBegin(); i; i++ )
112 {
113 lStack.push(
114 execExpr( (*i).begin() ) // Are they atomic?
115 );
116 }
117 if( lStack.getSize() != 1 )
118 {
119 throw Bu::ExceptionBase(
120 "Something went wrong, expression processing "
121 "left %d elements on stack, should be 1.",
122 lStack.getSize() );
123 }
124 }
125 break;
126
127 default:
128 sio << "?? branch ???: "
129 << (pBranch)->getType();
130 break;
131 }
132 }
133 else
134 {
135 const AstLeaf *pLeaf = dynamic_cast<const AstLeaf *>( *e );
136 switch( pLeaf->getType() )
137 {
138 case AstNode::typeVariable:
139 try
140 {
141 lStack.push(
142 rCont.getVariable( pLeaf->getStrValue() )
143 );
144 }
145 catch(...)
146 {
147 lStack.push( Variable() );
148 }
149 break;
150
151 case AstNode::typeVariableRef:
152 lStack.push(
153 Variable::mkRef( pLeaf->getStrValue() )
154 );
155 break;
156
157 case AstNode::typeString:
158 lStack.push(
159 rCont.expand( pLeaf->getStrValue() )
160 );
161 break;
162
163 case AstNode::typeInt:
164 lStack.push(
165 pLeaf->getIntValue()
166 );
167 break;
168
169 case AstNode::typeFloat:
170 lStack.push(
171 pLeaf->getFloatValue()
172 );
173 break;
174
175 case AstNode::typeBool:
176 lStack.push(
177 pLeaf->getBoolValue()
178 );
179 break;
180
181 case AstNode::typeVersion:
182 break;
183
184 case AstNode::typeNull:
185 lStack.push(
186 Variable()
187 );
188 break;
189
190 case AstNode::typeCmpEq:
191 {
192 Variable a, b;
193 a = lStack.peekPop();
194 b = lStack.peekPop();
195 lStack.push( Variable( a == b ) );
196 }
197 break;
198
199 case AstNode::typeCmpLt:
200 {
201 Variable a, b;
202 a = lStack.peekPop();
203 b = lStack.peekPop();
204 lStack.push( Variable( b < a ) );
205 }
206 break;
207
208 case AstNode::typeCmpGt:
209 {
210 Variable a, b;
211 a = lStack.peekPop();
212 b = lStack.peekPop();
213 lStack.push( Variable( b > a ) );
214 }
215 break;
216
217 case AstNode::typeCmpNe:
218 {
219 Variable a, b;
220 a = lStack.peekPop();
221 b = lStack.peekPop();
222 lStack.push( Variable( a != b ) );
223 }
224 break;
225
226 case AstNode::typeCmpLtEq:
227 {
228 Variable a, b;
229 a = lStack.peekPop();
230 b = lStack.peekPop();
231 lStack.push( Variable( b <= a ) );
232 }
233 break;
234
235 case AstNode::typeCmpGtEq:
236 {
237 Variable a, b;
238 a = lStack.peekPop();
239 b = lStack.peekPop();
240 lStack.push( Variable( b >= a ) );
241 }
242 break;
243
244 case AstNode::typeOpEq:
245 {
246 Variable ref, val;
247 val = lStack.peekPop();
248 ref = lStack.peekPop();
249 rCont.addVariable( ref.getString(), val );
250 lStack.push( val );
251 }
252 break;
253
254 case AstNode::typeOpPlusEq:
255 {
256 Variable ref, val;
257 val = lStack.peekPop();
258 ref = lStack.peekPop();
259 try
260 {
261 Variable &nVal = rCont.getVariable(
262 ref.getString()
263 );
264 nVal += val;
265 lStack.push( nVal );
266 } catch(...)
267 {
268 rCont.addVariable( ref.getString(), val );
269 lStack.push( val );
270 }
271 }
272 break;
273
274 case AstNode::typeOpPlusEqRaw:
275 {
276 Variable ref, val;
277 val = lStack.peekPop();
278 ref = lStack.peekPop();
279 try
280 {
281 Variable &nVal = rCont.getVariable(
282 ref.getString()
283 );
284 nVal << val;
285 lStack.push( nVal );
286 } catch(...)
287 {
288 rCont.addVariable( ref.getString(), val );
289 lStack.push( val );
290 }
291 }
292 break;
293
294 case AstNode::typeOpPlus:
295 {
296 Variable a, b;
297 a = lStack.peekPop();
298 b = lStack.peekPop();
299 lStack.push( Variable( b + a ) );
300 }
301 break;
302
303 case AstNode::typeOpMinus:
304 {
305 Variable a, b;
306 a = lStack.peekPop();
307 b = lStack.peekPop();
308 lStack.push( Variable( b - a ) );
309 }
310 break;
311
312 case AstNode::typeOpMultiply:
313 {
314 Variable a, b;
315 a = lStack.peekPop();
316 b = lStack.peekPop();
317 lStack.push( Variable( b * a ) );
318 }
319 break;
320
321 case AstNode::typeOpDivide:
322 {
323 Variable a, b;
324 a = lStack.peekPop();
325 b = lStack.peekPop();
326 lStack.push( Variable( b / a ) );
327 }
328 break;
329
330 case AstNode::typeOpNegate:
331 lStack.peek().doNegate();
332 break;
333
334 case AstNode::typeOpNot:
335 lStack.peek().doNot();
336 break;
337
338 default:
339 sio << "?? leaf ???: "
340 << (pLeaf)->getType();
341 break;
342 }
343 }
344 }
345
346 return lStack.peek();
347}
348
349void Runner::run()
350{
351 run( rAst.getNodeBegin() );
352
353 rCont.buildTargetTree( *this );
354
355 rCont.attachDefaults();
356 rCont.genDefaultActions();
357
358// rCont.writeTargetDot();
359}
360
361Variable Runner::run( AstBranch::NodeList::const_iterator n )
362{
363 /* Execute the top level code. */
364
365 Variable vReturn;
366 Bu::List<Ast::NodeList::const_iterator> sI;
367 sI.push( n );
368// for( Ast::NodeList::const_iterator i = rAst.getNodeBegin(); i; i++ )
369 while( !sI.isEmpty() )
370 {
371 while( !sI.isEmpty() && !(sI.peek()) )
372 {
373 sI.pop();
374 }
375 if( sI.isEmpty() )
376 break;
377 Ast::NodeList::const_iterator &i = sI.peek();
378 if( ((*i)->getType()&AstNode::typeClassMask) == AstNode::typeLeaf )
379 {
380 const AstLeaf *pExpr = dynamic_cast<const AstLeaf *>( *i );
381 switch( pExpr->getType() )
382 {
383 case AstNode::typeError:
384 {
385 Bu::FString sMsg = rCont.expand( pExpr->getStrValue() );
386 rCont.getView()->userError( sMsg.getStr() );
387 throw Bu::ExceptionBase( sMsg.getStr() );
388 }
389 break;
390
391 case AstNode::typeWarning:
392 rCont.getView()->userWarning(
393 rCont.expand( pExpr->getStrValue() )
394 );
395 break;
396
397 case AstNode::typeNotice:
398 rCont.getView()->userNotice(
399 rCont.expand( pExpr->getStrValue() )
400 );
401 break;
402
403 case AstNode::typeCondition:
404 break;
405
406 case AstNode::typeDisplay:
407 if( pCurTarget )
408 {
409 pCurTarget->setDisplay(
410 rCont.expand( pExpr->getStrValue() )
411 );
412 }
413 else if( pCurRule )
414 {
415 pCurRule->setDisplay(
416 rCont.expand( pExpr->getStrValue() )
417 );
418 }
419 break;
420/*
421 case AstNode::typeCondition:
422 if( pCurTarget )
423 {
424 if( pExpr->getStrValue() == "filetime" )
425 {
426 pCurTarget->setCondition(
427 new ConditionFileTime()
428 );
429 }
430 }
431 else if( pCurRule )
432 {
433 if( pExpr->getStrValue() == "filetime" )
434 {
435 pCurRule->setCondition(
436 new ConditionFileTime()
437 );
438 }
439 }
440 else
441 {
442 throw Bu::ExceptionBase(
443 "You can only set a condition in a target or rule.");
444 }
445 break;
446*/
447 default:
448 sio << "Leaf? " << (*i)->getType() << sio.nl;
449 break;
450 }
451 }
452 else
453 {
454 const AstBranch *pExpr = dynamic_cast<const AstBranch *>( *i );
455 switch( pExpr->getType() )
456 {
457 case AstNode::typeSet:
458 {
459 // This is effectively legacy, if we add the set
460 // keyword back in I want it to work.
461 doSet( pExpr );
462 }
463 break;
464
465 case AstNode::typeUnset:
466 {
467 AstBranch::NodeList::const_iterator n =
468 (*pExpr->getBranchBegin()).begin();
469 Bu::FString sVar = dynamic_cast<const AstLeaf *>(
470 *n )->getStrValue();
471 rCont.delVariable( sVar );
472 }
473 break;
474
475 case AstNode::typeIf:
476 {
477 AstBranch::BranchList::const_iterator b =
478 pExpr->getBranchBegin();
479
480 Variable v = execExpr( (*b).begin() );
481 if( v.getType() != Variable::typeBool )
482 {
483 throw Bu::ExceptionBase(
484 "If statement evaluated to non-boolean.");
485 }
486 b++;
487 if( v.getBool() )
488 {
489 i++;
490 sI.push( (*b).begin() );
491 continue;
492 }
493 else
494 {
495 b++;
496 if( b )
497 {
498 i++;
499 sI.push( (*b).begin() );
500 continue;
501 }
502 }
503 }
504 break;
505
506 case AstNode::typeFor:
507 {
508 AstBranch::BranchList::const_iterator b =
509 pExpr->getBranchBegin();
510 Bu::FString sVar = dynamic_cast<const AstLeaf *>(
511 (*b).first() )->getStrValue();
512 b++;
513 Variable v = execExpr( (*b).begin() );
514 b++;
515 for( VarList::const_iterator vi = v.getList().begin();
516 vi; vi++ )
517 {
518 rCont.addVariable( sVar, *vi );
519 run( (*b).begin() );
520 }
521 }
522 break;
523
524 case AstNode::typeFunction:
525 {
526 Variable vIn;
527 execFunc( pExpr, vIn );
528 }
529 break;
530
531 case AstNode::typeReturn:
532 vReturn = execExpr( (*pExpr->getBranchBegin()).begin() );
533 return vReturn;
534 break;
535
536 case AstNode::typeFunctionDef:
537 case AstNode::typeActionDef:
538 // We ignore these, we already dealt with them
539 break;
540
541 case AstNode::typeTarget:
542 // This actually runs exactly like a for loop, if there's
543 // only one item, then we only go once, if it's a list, go
544 // more than once :-P
545 if( pCurTarget == NULL )
546 {
547 AstBranch::BranchList::const_iterator b =
548 pExpr->getBranchBegin();
549 Variable vLoop = execExpr( (*b).begin() );
550 b++;
551 if( vLoop.getType() == Variable::typeString )
552 {
553 rCont.addTarget(
554 buildTarget(
555 vLoop.getString(), (*b).begin()
556 )
557 );
558 }
559 else if( vLoop.getType() == Variable::typeList )
560 {
561 for( VarList::iterator i = vLoop.begin(); i; i++ )
562 {
563 rCont.addTarget(
564 buildTarget(
565 (*i).getString(), (*b).begin()
566 )
567 );
568 }
569 }
570 }
571 else
572 {
573 throw Bu::ExceptionBase(
574 "You cannot declare a target within "
575 "a target decleration.");
576 }
577 break;
578
579 case AstNode::typeRuleDef:
580 if( pCurRule == NULL )
581 {
582 AstBranch::BranchList::const_iterator b =
583 pExpr->getBranchBegin();
584 Bu::FString sName = dynamic_cast<const AstLeaf *>(
585 (*b).first()
586 )->getStrValue();
587 b++;
588 rCont.addRule( buildRule( sName, (*b).begin() ) );
589 }
590 else
591 {
592 throw Bu::ExceptionBase(
593 "You cannot declare a rule within "
594 "a rule decleration.");
595 }
596 break;
597
598 case AstNode::typeInput:
599 if( pCurTarget != NULL )
600 {
601 Variable vRet = execExpr(
602 (*pExpr->getBranchBegin()).begin()
603 );
604 if( vRet.getType() == Variable::typeString )
605 {
606 pCurTarget->addInput( vRet.getString() );
607 }
608 else if( vRet.getType() == Variable::typeList )
609 {
610 for( VarList::iterator i = vRet.begin(); i; i++ )
611 {
612 pCurTarget->addInput(
613 (*i).getString()
614 );
615 }
616 }
617 }
618 else if( pCurRule != NULL )
619 {
620 pCurRule->setInput( pExpr );
621 }
622 else
623 {
624 throw Bu::ExceptionBase(
625 "input can only occur within a target or rule.");
626 }
627 break;
628
629 case AstNode::typeRequires:
630 if( pCurTarget != NULL )
631 {
632 Variable vRet = execExpr(
633 (*pExpr->getBranchBegin()).begin()
634 );
635 if( vRet.getType() == Variable::typeString )
636 {
637 pCurTarget->addRequires( vRet.getString() );
638 }
639 else if( vRet.getType() == Variable::typeList )
640 {
641 for( VarList::iterator i = vRet.begin(); i; i++ )
642 {
643 pCurTarget->addRequires(
644 (*i).getString()
645 );
646 }
647 }
648 }
649 else if( pCurRule != NULL )
650 {
651 pCurRule->addRequires( pExpr );
652 }
653 else
654 {
655 throw Bu::ExceptionBase(
656 "requires can only occur within a target or rule.");
657 }
658 break;
659
660 case AstNode::typeRule:
661 if( pCurTarget )
662 {
663 pCurTarget->setRule(
664 dynamic_cast<const AstLeaf *>(
665 (*pExpr->getBranchBegin()).first()
666 )->getStrValue()
667 );
668 }
669 else
670 {
671 throw Bu::ExceptionBase(
672 "rule can only occur within a target.");
673 }
674 break;
675
676 case AstNode::typeProfile:
677 if( pCurTarget )
678 {
679 pCurTarget->addProfile( pExpr );
680 }
681 else if( pCurRule )
682 {
683 pCurRule->addProfile( pExpr );
684 }
685 else
686 {
687 throw Bu::ExceptionBase(
688 "profile can only occur within a target or rule.");
689 }
690 break;
691
692 case AstNode::typeOutput:
693 if( pCurRule )
694 {
695 pCurRule->addOutput( pExpr );
696 }
697 else
698 {
699 throw Bu::ExceptionBase(
700 "output can only occur within a rule.");
701 }
702 break;
703
704 case AstNode::typeProcessTarget:
705 {
706 AstBranch::BranchList::const_iterator b =
707 pExpr->getBranchBegin();
708 Bu::FString sProfile = dynamic_cast<const AstLeaf *>(
709 (*b).first()
710 )->getStrValue();
711 b++;
712 Variable vTargs = execExpr( (*b).begin() );
713 if( vTargs.getType() == Variable::typeString )
714 {
715 rCont.getTarget( vTargs.getString() )->process(
716 *this, sProfile
717 );
718 }
719 else if( vTargs.getType() == Variable::typeList )
720 {
721 for( VarList::iterator v = vTargs.begin();
722 v; v++ ) {
723 rCont.getTarget( (*v).getString() )->process(
724 *this, sProfile
725 );
726 }
727 }
728 }
729 break;
730
731 case AstNode::typeTag:
732 if( pCurTarget )
733 {
734 AstBranch::BranchList::const_iterator b =
735 pExpr->getBranchBegin();
736 Variable vTags = execExpr( (*b).begin() );
737 if( vTags.getType() == Variable::typeList )
738 {
739 for( VarList::iterator i = vTags.begin(); i; i++ )
740 {
741 rCont.addTargetToTag( pCurTarget, (*i).toString() );
742 }
743 }
744 else
745 {
746 Bu::FString sTag = vTags.toString();
747 if( sTag )
748 {
749 rCont.addTargetToTag( pCurTarget, sTag );
750 }
751 else
752 {
753 throw Bu::ExceptionBase(
754 "A tag evaluted to empty string."
755 );
756 }
757 }
758 }
759 else if( pCurRule )
760 {
761 AstBranch::BranchList::const_iterator b =
762 pExpr->getBranchBegin();
763 Variable vTags = execExpr( (*b).begin() );
764 if( vTags.getType() == Variable::typeList )
765 {
766 for( VarList::iterator i = vTags.begin(); i; i++ )
767 {
768 pCurRule->addTag( (*i).toString() );
769 }
770 }
771 else
772 {
773 Bu::FString sTag = vTags.toString();
774 if( sTag )
775 {
776 pCurRule->addTag( sTag );
777 }
778 else
779 {
780 throw Bu::ExceptionBase(
781 "A tag evaluted to empty string."
782 );
783 }
784 }
785 }
786 else
787 {
788 throw Bu::ExceptionBase(
789 "tag can only occur within a target or rule.");
790 }
791 break;
792
793 case AstNode::typeExpr:
794 execExpr( (*pExpr->getBranchBegin()).begin() );
795 break;
796
797 default:
798 sio << "Branch? " << (*i)->getType() << sio.nl;
799 break;
800 }
801 }
802
803 i++;
804 }
805
806 return vReturn;
807}
808
809void Runner::execProfile( Target *pTarget, const Bu::FString &sProfile )
810{
811 rCont.pushScope( pTarget->getVars() );
812 run( (*(pTarget->getProfile( sProfile )->getRoot()->
813 getBranchBegin()+1)).begin() );
814 rCont.popScope();
815}
816
817void Runner::execAction( const Bu::FString &sName )
818{
819 try
820 {
821 Action *pAct = rCont.getAction( sName );
822
823 pAct->call( this );
824 }
825 catch( Bu::HashException &e )
826 {
827 Bu::FString sError("No such action '" + sName + "' found.");
828 rCont.getView()->sysError( sError );
829 }
830}
831
832Context &Runner::getContext()
833{
834 return rCont;
835}
836
837Target *Runner::buildTarget( const Bu::FString &sOutput,
838 AstBranch::NodeList::const_iterator n )
839{
840 Target *pTrg = NULL;
841 try
842 {
843 pTrg = rCont.getTarget( sOutput );
844 rCont.pushScope( pTrg->getVars() );
845 }
846 catch( Bu::HashException &e )
847 {
848 pTrg = new Target( sOutput, true );
849 rCont.pushScope();
850 }
851
852 // sio << " (target) \"" << sOutput << "\" explicit." << sio.nl;
853
854 rCont.addVariable("OUTPUT", sOutput );
855 pCurTarget = pTrg;
856 run( n );
857
858 rCont.addVariable("INPUT", pTrg->getInputList() );
859 pCurTarget = NULL;
860
861 pTrg->setVars( rCont.getScope() );
862 rCont.popScope();
863
864 return pTrg;
865}
866
867Rule *Runner::buildRule( const Bu::FString &sName,
868 AstBranch::NodeList::const_iterator n )
869{
870 Rule *pRule = new Rule( sName );
871
872 rCont.pushScope();
873 pCurRule = pRule;
874 run( n );
875 rCont.popScope();
876 pCurRule = NULL;
877
878 return pRule;
879}
880
881Variable Runner::doSet( const AstBranch *pRoot )
882{
883 AstBranch::NodeList::const_iterator n =
884 (*pRoot->getBranchBegin()).begin();
885 Bu::FString sVar = dynamic_cast<const AstLeaf *>( *n )->getStrValue();
886 n++;
887 const AstLeaf *pLeaf = dynamic_cast<const AstLeaf *>( *n );
888 n++;
889 Variable v = execExpr( n );
890
891 switch( pLeaf->getType() )
892 {
893 case AstNode::typeOpEq:
894 rCont.addVariable( sVar, v );
895 break;
896
897 case AstNode::typeOpPlusEq:
898 try
899 {
900 rCont.getVariable( sVar ) += v;
901 } catch(...)
902 {
903 rCont.addVariable( sVar, v );
904 }
905 break;
906
907 case AstNode::typeOpPlusEqRaw:
908 try
909 {
910 rCont.getVariable( sVar ) << v;
911 } catch(...)
912 {
913 rCont.addVariable( sVar, v );
914 }
915 break;
916
917 default: break;
918 }
919
920 return v;
921}
922
diff --git a/src/runner.h b/src/runner.h
new file mode 100644
index 0000000..98894da
--- /dev/null
+++ b/src/runner.h
@@ -0,0 +1,43 @@
1#ifndef RUNNER_H
2#define RUNNER_H
3
4#include "astbranch.h"
5
6class Runner
7{
8public:
9 Runner( class Ast &rAst, class Context &rCont );
10 virtual ~Runner();
11
12 /**
13 * Run through and pull out all of the functions. Maybe more later.
14 */
15 void initialize();
16 class Variable execExpr( AstBranch::NodeList::const_iterator e );
17 class Variable execExpr( AstBranch::NodeList::const_iterator e,
18 const class Variable &vIn );
19 void run();
20 Variable run( AstBranch::NodeList::const_iterator n );
21 class Variable execFunc( const class AstBranch *pFunc,
22 class Variable &vIn );
23 void execProfile( class Target *pTarget, const Bu::FString &sProfile );
24 void execAction( const Bu::FString &sName );
25
26 Context &getContext();
27
28private:
29 class Target *buildTarget( const Bu::FString &sOutput,
30 AstBranch::NodeList::const_iterator n );
31 class Rule *buildRule( const Bu::FString &sName,
32 AstBranch::NodeList::const_iterator n );
33 void attachDefaults();
34 Variable doSet( const AstBranch *pRoot );
35
36private:
37 class Ast &rAst;
38 class Context &rCont;
39 Target *pCurTarget;
40 Rule *pCurRule;
41};
42
43#endif
diff --git a/src/target.cpp b/src/target.cpp
new file mode 100644
index 0000000..f3e54b7
--- /dev/null
+++ b/src/target.cpp
@@ -0,0 +1,402 @@
1#include "target.h"
2#include "variable.h"
3#include "condition.h"
4#include "astleaf.h"
5#include "astbranch.h"
6#include "runner.h"
7#include "context.h"
8#include "profile.h"
9#include "view.h"
10
11#include <bu/membuf.h>
12#include <bu/formatter.h>
13#include <bu/heap.h>
14
15#include <bu/sio.h>
16using namespace Bu;
17
18Target::Target( bool bExplicit ) :
19 bExplicit( bExplicit ),
20 bRun( false ),
21 iDepCount( 0 )
22{
23}
24
25Target::Target( const Bu::FString &sOutput, bool bExplicit ) :
26 bExplicit( bExplicit ),
27 lsOutput( sOutput ),
28 iDepCount( 0 )
29{
30}
31
32Target::~Target()
33{
34}
35
36void Target::addInput( const Bu::FString &sInput )
37{
38 lsInput.append( sInput );
39}
40
41const StrList &Target::getInputList() const
42{
43 return lsInput;
44}
45
46void Target::resetInputList( const StrList &lInputs )
47{
48 lsInput = lInputs;
49 if( lsInput.getSize() == 1 )
50 {
51 hVars.insert("INPUT", lsInput.first() );
52 }
53 else
54 {
55 Variable vInput( Variable::typeList );
56 for( StrList::iterator i = lsInput.begin(); i; i++ )
57 {
58 vInput.append( Variable( *i ) );
59 }
60 hVars.insert("INPUT", vInput );
61 }
62}
63
64void Target::addRequires( const Bu::FString &sReq )
65{
66 lsRequires.append( sReq );
67}
68
69void Target::addRequires( const AstBranch *pBr )
70{
71 lbRequires.append( pBr );
72}
73
74const StrList &Target::getRequiresList() const
75{
76 return lsRequires;
77}
78
79void Target::buildRequires( Runner &r )
80{
81 r.getContext().getView()->buildRequires( *this );
82 r.getContext().pushScope( hVars );
83 for( AstBranchList::iterator i = lbRequires.begin(); i; i++ )
84 {
85 Variable v = r.execExpr( (*(*i)->getBranchBegin()).begin() );
86 if( v.getType() == Variable::typeList )
87 {
88 for( VarList::iterator j = v.begin(); j; j++ )
89 {
90 Bu::FString sReq = (*j).toString();
91 addRequires( sReq );
92 try
93 {
94 addDep( r.getContext().getTarget( sReq ) );
95 }
96 catch(...) { }
97 }
98 }
99 else
100 {
101 Bu::FString sReq = v.toString();
102 addRequires( sReq );
103 try
104 {
105 addDep( r.getContext().getTarget( sReq ) );
106 }
107 catch(...) { }
108 }
109 }
110 r.getContext().popScope();
111}
112
113void Target::addOutput( const Bu::FString &sOutput )
114{
115 lsOutput.append( sOutput );
116}
117
118const StrList &Target::getOutputList() const
119{
120 return lsOutput;
121}
122
123void Target::setPrefix( const Bu::FString &sPrefix )
124{
125 this->sPrefix = sPrefix;
126}
127
128const Bu::FString &Target::getPrefix() const
129{
130 return sPrefix;
131}
132
133void Target::setRule( const Bu::FString &sRule )
134{
135 this->sRule = sRule;
136}
137
138const Bu::FString &Target::getRule() const
139{
140 return sRule;
141}
142
143bool Target::hasRule() const
144{
145 return !sRule.isEmpty();
146}
147
148bool Target::isExplicit() const
149{
150 return bExplicit;
151}
152
153void Target::addDep( Target *pDep )
154{
155 lDeps.append( pDep );
156}
157
158const TargetList &Target::getDepList() const
159{
160 return lDeps;
161}
162
163void Target::addProfile( const class AstBranch *pProfRoot )
164{
165 Profile *pProf = new Profile( pProfRoot );
166 hProfiles.insert( pProf->getName(), pProf );
167 /*
168 hProfiles.insert(
169 dynamic_cast<const AstLeaf *>(
170 (*pProfRoot->getBranchBegin()).first()
171 )->getStrValue(),
172 pProfRoot
173 );
174 */
175}
176
177void Target::addProfile( const class Profile *pSrc )
178{
179 hProfiles.insert( pSrc->getName(), new Profile( *pSrc ) );
180}
181
182bool Target::hasProfile( const Bu::FString &sName ) const
183{
184 return hProfiles.has( sName );
185}
186
187const Profile *Target::getProfile( const Bu::FString &sName ) const
188{
189 return hProfiles.get( sName );
190}
191
192void Target::setVars( const VarHash &hNewVars )
193{
194 hVars = hNewVars;
195}
196
197const VarHash &Target::getVars() const
198{
199 return hVars;
200}
201
202void Target::setDisplay( const Bu::FString &sNewDisplay )
203{
204 if( !sDisplay )
205 sDisplay = sNewDisplay;
206}
207
208const Bu::FString &Target::getDisplay() const
209{
210 return sDisplay;
211}
212
213void Target::process( class Runner &r, const Bu::FString &sProfile )
214{
215 r.getContext().getView()->beginTarget( sProfile, *this );
216 bRun = true;
217 bool bShouldExec = false;
218
219 for( TargetList::iterator i = lDeps.begin(); i; i++ )
220 {
221 if( (*i)->bRun )
222 continue;
223
224 // TODO: This is important, in the future, it may be possible for a
225 // target to be triggered by multiple dependant targets, to cover for
226 // this the below mergeUnder should be *TEMPORARY* and the target
227 // that was marged to be reset post processing.
228 (*i)->mergeUnder( hVars );
229 (*i)->process( r, sProfile );
230 }
231 try
232 {
233 bShouldExec = hProfiles.get( sProfile )->shouldExec( r, *this );
234 }
235 catch( Bu::HashException &e )
236 {
237 }
238
239 if( !bShouldExec )
240 {
241 r.getContext().getView()->skipTarget( sProfile, *this );
242 }
243 else
244 {
245 r.getContext().getView()->processTarget( sProfile, *this );
246 r.execProfile( this, sProfile );
247 }
248
249 r.getContext().getView()->endTarget();
250}
251
252void Target::mergeUnder( const VarHash &hNewVars )
253{
254 for( VarHash::const_iterator i = hNewVars.begin(); i; i++ )
255 {
256 if( !hVars.has( i.getKey() ) )
257 {
258 hVars.insert( i.getKey(), i.getValue() );
259 }
260 }
261}
262
263bool Target::hasRun()
264{
265 return bRun;
266}
267
268void Target::mergeUnder( const Target *pSrc )
269{
270 // If either are explicit, then it's explicit
271 bExplicit = bExplicit || pSrc->bExplicit;
272
273 merge( lsInput, pSrc->lsInput );
274 merge( lsRequires, pSrc->lsRequires );
275 merge( lsOutput, pSrc->lsOutput );
276
277 if( !sPrefix )
278 sPrefix = pSrc->sPrefix;
279
280 sRule = pSrc->sRule;
281
282 mergeUnder( pSrc->hVars );
283
284 // Deps? They should be computed much after merging anyway, peh!
285
286 for( ProfileHash::const_iterator i = pSrc->hProfiles.begin(); i; i++ )
287 {
288 if( !hProfiles.has( i.getKey() ) )
289 {
290 hProfiles.insert( i.getKey(), i.getValue() );
291 }
292 }
293
294 if( !sDisplay )
295 sDisplay = pSrc->sDisplay;
296
297 // Now we need to reset our vars.
298 hVars.insert("INPUT", lsInput );
299 hVars.insert("REQUIRES", lsRequires );
300 hVars.insert("OUTPUT", lsOutput );
301}
302
303void Target::merge( StrList &lOut, const StrList &lIn )
304{
305 Bu::Heap<Bu::FString> hStr;
306 for( StrList::const_iterator i = lOut.begin(); i; i++ )
307 {
308 hStr.enqueue( *i );
309 }
310 for( StrList::const_iterator i = lIn.begin(); i; i++ )
311 {
312 hStr.enqueue( *i );
313 }
314
315 lOut.clear();
316
317 if( hStr.isEmpty() )
318 return;
319
320 lOut.append( hStr.dequeue() );
321 while( !hStr.isEmpty() )
322 {
323 if( hStr.peek() == lOut.last() )
324 {
325 hStr.dequeue();
326 }
327 else
328 {
329 lOut.append( hStr.dequeue() );
330 }
331 }
332}
333
334void Target::resetRun( bool bHasRun )
335{
336 bRun = bHasRun;
337
338 for( TargetList::iterator i = lDeps.begin(); i; i++ )
339 {
340 (*i)->resetRun( bHasRun );
341 }
342}
343
344void Target::setDepCount()
345{
346 bRun = true;
347 iDepCount = 1;
348 for( TargetList::iterator i = lDeps.begin(); i; i++ )
349 {
350 if( (*i)->bRun )
351 {
352 continue;
353 }
354 (*i)->setDepCount();
355 iDepCount += (*i)->getDepCount();
356 }
357}
358
359int Target::getDepCount() const
360{
361 return iDepCount;
362}
363
364void Target::collapseDeps()
365{
366 if( lDeps.getSize() <= 1 )
367 return;
368 Bu::Hash<ptrdiff_t, bool> hDep;
369 for( TargetList::iterator i = lDeps.begin(); i; i++ )
370 {
371 if( hDep.has( (ptrdiff_t)*i ) )
372 {
373 lDeps.erase( i );
374 i--;
375 }
376 else
377 {
378 hDep.insert( (ptrdiff_t)*i, true );
379 }
380 }
381}
382
383Bu::Formatter &operator<<( Bu::Formatter &f, const Target &t )
384{
385 f.incIndent();
386 f << f.nl << "Input = " << t.lsInput << "," << f.nl
387 << "Requires = " << t.lsRequires << "," << f.nl
388 << "Output = \"" << t.lsOutput << "\"," << f.nl
389 << "Prefix = \"" << t.sPrefix << "\"," << f.nl
390 << "Rule = \"" << t.sRule << "\"," << f.nl
391 << "Explicit = " << t.bExplicit << "," << f.nl
392 << "Vars = " << t.hVars
393 << f.nl;
394 f.decIndent();
395 return f;
396}
397
398template<> Bu::Formatter &Bu::operator<< <Target>( Bu::Formatter &f, const Target *t )
399{
400 return f << (*t);
401}
402
diff --git a/src/target.h b/src/target.h
new file mode 100644
index 0000000..766366a
--- /dev/null
+++ b/src/target.h
@@ -0,0 +1,89 @@
1#ifndef TARGET_H
2#define TARGET_H
3
4#include "types.h"
5#include <bu/formatter.h>
6
7class Target
8{
9 friend Bu::Formatter &operator<<( Bu::Formatter &f, const Target &t );
10public:
11 Target( bool bExplicit );
12 Target( const Bu::FString &sOutput, bool bExplicit );
13 virtual ~Target();
14
15 void addInput( const Bu::FString &sInput );
16 const StrList &getInputList() const;
17 void resetInputList( const StrList &lInputs );
18
19 void addRequires( const Bu::FString &sReq );
20 void addRequires( const AstBranch *pBr );
21 const StrList &getRequiresList() const;
22 void buildRequires( class Runner &r );
23
24 void addOutput( const Bu::FString &sOutput );
25 const StrList &getOutputList() const;
26
27 void setPrefix( const Bu::FString &sPrefix );
28 const Bu::FString &getPrefix() const;
29
30 void setRule( const Bu::FString &sRule );
31 const Bu::FString &getRule() const;
32 bool hasRule() const;
33
34 bool isExplicit() const;
35
36 void addDep( Target *pDep );
37 const TargetList &getDepList() const;
38
39 void addProfile( const class AstBranch *pProfRoot );
40 void addProfile( const class Profile *pSrc );
41 bool hasProfile( const Bu::FString &sName ) const;
42 const class Profile *getProfile( const Bu::FString &sName ) const;
43
44 void setVars( const VarHash &hNewVars );
45 const VarHash &getVars() const;
46
47 void setDisplay( const Bu::FString &sNewDisplay );
48 const Bu::FString &getDisplay() const;
49
50 void process( class Runner &r, const Bu::FString &sProfile );
51
52 void mergeUnder( const Target *pSrc );
53
54 bool hasRun();
55
56 void resetRun( bool bHasRun=true );
57 void setDepCount();
58 int getDepCount() const;
59
60 void collapseDeps();
61
62private:
63 void mergeUnder( const VarHash &hVars );
64 void merge( StrList &lOut, const StrList &lIn );
65
66private:
67 bool bExplicit;
68 StrList lsInput;
69 StrList lsRequires;
70 StrList lsOutput;
71 Bu::FString sPrefix;
72 Bu::FString sRule;
73 VarHash hVars;
74 TargetList lDeps;
75 ProfileHash hProfiles;
76 Bu::FString sDisplay;
77 bool bRun;
78 AstBranchList lbRequires;
79 int iDepCount;
80};
81
82Bu::Formatter &operator<<( Bu::Formatter &f, const Target &t );
83
84namespace Bu
85{
86 template<> Bu::Formatter &operator<< <Target>( Bu::Formatter &f, const Target *t );
87};
88
89#endif
diff --git a/src/types.h b/src/types.h
new file mode 100644
index 0000000..e405e35
--- /dev/null
+++ b/src/types.h
@@ -0,0 +1,26 @@
1#ifndef TYPES_H
2#define TYPES_H
3
4#include "bu/fstring.h"
5#include "bu/list.h"
6#include "bu/hash.h"
7
8typedef Bu::List<Bu::FString> StrList;
9
10class Variable;
11typedef Bu::List<Variable> VarList;
12typedef Bu::Hash<Bu::FString, Variable> VarHash;
13
14class Condition;
15
16class Target;
17typedef Bu::List<Target *> TargetList;
18class Profile;
19typedef Bu::Hash<Bu::FString, Profile *> ProfileHash;
20
21class AstNode;
22class AstBranch;
23class AstLeaf;
24typedef Bu::List<const AstBranch *> AstBranchList;
25
26#endif
diff --git a/src/variable.cpp b/src/variable.cpp
new file mode 100644
index 0000000..99bac59
--- /dev/null
+++ b/src/variable.cpp
@@ -0,0 +1,892 @@
1#include "variable.h"
2#include "astleaf.h"
3#include "bu/sio.h"
4using Bu::sio;
5
6#include <stdlib.h>
7
8Variable::Variable() :
9 eType( typeNone )
10{
11 memset( &uVal, 0, sizeof(uVal) );
12}
13
14Variable::Variable( Type t ) :
15 eType( t )
16{
17 memset( &uVal, 0, sizeof(uVal) );
18 if( eType == typeString || eType == typeRef )
19 {
20 uVal.sVal = new Bu::FString;
21 }
22 else if( eType == typeList )
23 {
24 uVal.lVal = new VarList;
25 }
26}
27
28Variable::Variable( int iVal ) :
29 eType( typeInt )
30{
31 memset( &uVal, 0, sizeof(uVal) );
32 uVal.iVal = iVal;
33}
34
35Variable::Variable( double fVal ) :
36 eType( typeFloat )
37{
38 memset( &uVal, 0, sizeof(uVal) );
39 uVal.fVal = fVal;
40}
41
42Variable::Variable( bool bVal ) :
43 eType( typeBool )
44{
45 memset( &uVal, 0, sizeof(uVal) );
46 uVal.bVal = bVal;
47}
48
49Variable::Variable( const Bu::FString &sVal ) :
50 eType( typeString )
51{
52 memset( &uVal, 0, sizeof(uVal) );
53 uVal.sVal = new Bu::FString( sVal );
54}
55
56Variable::Variable( const char *sVal ) :
57 eType( typeString )
58{
59 memset( &uVal, 0, sizeof(uVal) );
60 uVal.sVal = new Bu::FString( sVal );
61}
62
63Variable::Variable( const Variable &v ) :
64 eType( v.eType )
65{
66 memset( &uVal, 0, sizeof(uVal) );
67 if( eType == typeString || eType == typeRef )
68 {
69 uVal.sVal = new Bu::FString( *v.uVal.sVal );
70 }
71 else if( eType == typeList )
72 {
73 uVal.lVal = new VarList( *v.uVal.lVal );
74 }
75 else
76 {
77 uVal = v.uVal;
78 }
79}
80
81Variable::Variable( const class AstLeaf &l )
82{
83 switch( l.getDataType() )
84 {
85 case AstNode::typeDataInt:
86 eType = typeInt;
87 uVal.iVal = l.getIntValue();
88 break;
89
90 case AstNode::typeDataFloat:
91 eType = typeFloat;
92 uVal.fVal = l.getFloatValue();
93 break;
94
95 case AstNode::typeDataBool:
96 eType = typeBool;
97 uVal.bVal = l.getBoolValue();
98 break;
99
100 case AstNode::typeDataString:
101 eType = typeString;
102 uVal.sVal = new Bu::FString( l.getStrValue() );
103 break;
104
105 case AstNode::typeDataNone:
106 eType = typeNone;
107 memset( &uVal, 0, sizeof(uVal) );
108 break;
109
110 default:
111 sio << "Unhandled type <<!>>" << sio.nl << sio.nl;
112 break;
113 }
114}
115
116Variable::Variable( const StrList &lst )
117{
118 if( lst.getSize() == 1 )
119 {
120 eType = typeString;
121 uVal.sVal = new Bu::FString( lst.first() );
122 }
123 else
124 {
125 eType = typeList;
126 uVal.lVal = new VarList();
127 for( StrList::const_iterator i = lst.begin(); i; i++ )
128 {
129 uVal.lVal->append( Variable( *i ) );
130 }
131 }
132}
133
134Variable::Variable( const VarList &lst )
135{
136 eType = typeList;
137 uVal.lVal = new VarList( lst );
138}
139
140Variable::~Variable()
141{
142 if( eType == typeString || eType == typeRef )
143 {
144 delete uVal.sVal;
145 }
146 else if( eType == typeList )
147 {
148 delete uVal.lVal;
149 }
150}
151
152Variable Variable::mkRef( const Bu::FString &sVal )
153{
154 Variable v( typeRef );
155 (*v.uVal.sVal) = sVal;
156 return v;
157}
158
159Variable::Type Variable::getType() const
160{
161 return eType;
162}
163
164int Variable::getInt() const
165{
166 if( eType != typeInt ) throw Bu::ExceptionBase("Wrong variable type.");
167 return uVal.iVal;
168}
169
170double Variable::getFloat() const
171{
172 if( eType != typeFloat ) throw Bu::ExceptionBase("Wrong variable type.");
173 return uVal.fVal;
174}
175
176bool Variable::getBool() const
177{
178 if( eType != typeBool ) throw Bu::ExceptionBase("Wrong variable type.");
179 return uVal.bVal;
180}
181
182const Bu::FString &Variable::getString() const
183{
184 if( eType != typeString && eType != typeRef ) throw Bu::ExceptionBase("Wrong variable type.");
185 return *uVal.sVal;
186}
187
188const VarList &Variable::getList() const
189{
190 if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type.");
191 return *uVal.lVal;
192}
193
194int Variable::toInt() const
195{
196 switch( eType )
197 {
198 case typeInt:
199 return uVal.iVal;
200
201 case typeFloat:
202 return (int)uVal.fVal;
203
204 case typeBool:
205 return (uVal.bVal)?(1):(0);
206
207 case typeString:
208 case typeRef:
209 return strtol( uVal.sVal->getStr(), NULL, 0 );
210
211 default:
212 return 0;
213 }
214 return 0;
215}
216
217double Variable::toFloat() const
218{
219 switch( eType )
220 {
221 case typeInt:
222 return (double)uVal.iVal;
223
224 case typeFloat:
225 return uVal.fVal;
226
227 case typeBool:
228 return (uVal.bVal)?(1.0):(0.0);
229
230 case typeString:
231 case typeRef:
232 return strtod( uVal.sVal->getStr(), NULL );
233
234 default:
235 return 0.0;
236 }
237 return 0.0;
238}
239
240bool Variable::toBool() const
241{
242 switch( eType )
243 {
244 case typeInt:
245 return uVal.iVal != 0;
246
247 case typeFloat:
248 return uVal.fVal != 0.0;
249
250 case typeBool:
251 return uVal.bVal;
252
253 case typeString:
254 case typeRef:
255 return (*uVal.sVal) == "true";
256
257 case typeList:
258 return !(*uVal.lVal).isEmpty();
259
260 default:
261 return false;
262 }
263 return false;
264}
265
266Bu::FString Variable::toString() const
267{
268 Bu::FString sRet;
269 switch( eType )
270 {
271 case typeNone:
272 // No type, no data, we return empty string
273 break;
274
275 case typeInt:
276 sRet.format("%d", uVal.iVal );
277 break;
278
279 case typeFloat:
280 sRet.format("%f", uVal.fVal );
281 break;
282
283 case typeBool:
284 sRet = (uVal.bVal)?("true"):("false");
285 break;
286
287 case typeString:
288 case typeRef:
289 sRet = *uVal.sVal;
290 break;
291
292 case typeList:
293 {
294 for( VarList::const_iterator i = uVal.lVal->begin(); i; i++ )
295 {
296 if( i != uVal.lVal->begin() )
297 sRet += " ";
298 sRet += (*i).toString();
299 }
300 }
301 break;
302
303 case typeVersion:
304 break;
305 }
306
307 return sRet;
308}
309
310VarList Variable::toList() const
311{
312 if( eType == typeList )
313 return *this;
314 return VarList( *this );
315}
316
317Variable Variable::toType( Type eNewType ) const
318{
319 switch( eNewType )
320 {
321 case typeNone:
322 return Variable();
323
324 case typeBool:
325 return Variable( toBool() );
326
327 case typeInt:
328 return Variable( toInt() );
329
330 case typeFloat:
331 return Variable( toFloat() );
332
333 case typeVersion:
334 return Variable();
335
336 case typeString:
337 return Variable( toString() );
338
339 case typeList:
340 return Variable( toList() );
341
342 case typeRef:
343 return Variable::mkRef( toString() );
344 }
345 throw Bu::ExceptionBase("Unhandled case in Variable toType");
346}
347
348void Variable::append( const Variable &v )
349{
350 if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type.");
351
352 if( v.eType == typeList )
353 {
354 uVal.lVal->append( *v.uVal.lVal );
355 }
356 else
357 {
358 uVal.lVal->append( v );
359 }
360}
361
362VarList::iterator Variable::begin()
363{
364 if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type.");
365
366 return uVal.lVal->begin();
367}
368
369VarList::const_iterator Variable::begin() const
370{
371 if( eType != typeList ) throw Bu::ExceptionBase("Wrong variable type.");
372
373 return const_cast<const VarList *>(uVal.lVal)->begin();
374}
375
376void Variable::doNegate()
377{
378 switch( eType )
379 {
380 case typeNone:
381 break;
382
383 case typeBool:
384 throw Bu::ExceptionBase("You cannot negate boolean values.");
385
386 case typeInt:
387 uVal.iVal = -uVal.iVal;
388 break;
389
390 case typeFloat:
391 uVal.fVal = -uVal.fVal;
392 break;
393
394 case typeVersion:
395 throw Bu::ExceptionBase("You cannot negate version values.");
396
397 case typeString:
398 throw Bu::ExceptionBase("You cannot negate string values.");
399
400 case typeList:
401 throw Bu::ExceptionBase("You cannot negate list values.");
402
403 case typeRef:
404 throw Bu::ExceptionBase("You cannot negate reference values.");
405 }
406}
407
408void Variable::doNot()
409{
410 bool bVal = !toBool();
411 reset( typeBool );
412 uVal.bVal = bVal;
413}
414
415const Variable &Variable::operator=( const Variable &rhs )
416{
417 reset( rhs.eType );
418 if( rhs.eType == typeString || rhs.eType == typeRef )
419 {
420 uVal.sVal = new Bu::FString( *rhs.uVal.sVal );
421 }
422 else if( rhs.eType == typeList )
423 {
424 uVal.lVal = new VarList( *rhs.uVal.lVal );
425 }
426 else
427 {
428 uVal = rhs.uVal;
429 }
430
431 return *this;
432}
433
434const Variable &Variable::operator=( const int &rhs )
435{
436 reset( typeInt );
437 uVal.iVal = rhs;
438
439 return *this;
440}
441
442const Variable &Variable::operator=( const double &rhs )
443{
444 reset( typeFloat );
445 uVal.fVal = rhs;
446
447 return *this;
448}
449
450const Variable &Variable::operator=( const bool &rhs )
451{
452 reset( typeBool );
453 uVal.bVal = rhs;
454
455 return *this;
456}
457
458const Variable &Variable::operator=( const Bu::FString &rhs )
459{
460 reset( typeString );
461 uVal.sVal = new Bu::FString( rhs );
462
463 return *this;
464}
465
466const Variable &Variable::operator+=( const Variable &rhs )
467{
468 switch( eType )
469 {
470 case typeNone:
471 reset( rhs.eType );
472 if( eType == typeString || eType == typeRef )
473 {
474 uVal.sVal = new Bu::FString( *rhs.uVal.sVal );
475 }
476 else if( eType == typeList )
477 {
478 uVal.lVal = new VarList( *rhs.uVal.lVal );
479 }
480 else
481 {
482 uVal = rhs.uVal;
483 }
484 break;
485
486 case typeInt:
487 uVal.iVal += rhs.getInt();
488 break;
489
490 case typeFloat:
491 uVal.fVal += rhs.getFloat();
492 break;
493
494 case typeBool:
495 throw Bu::ExceptionBase("Can't += with a boolean...");
496 break;
497
498 case typeString:
499 uVal.sVal->append(" ");
500 uVal.sVal->append( rhs.getString() );
501 break;
502
503 case typeList:
504 uVal.lVal->append( rhs.getList() );
505 break;
506
507 case typeVersion:
508 break;
509
510 default:
511 break;
512 }
513 return *this;
514}
515
516const Variable &Variable::operator<<( const Variable &rhs )
517{
518 switch( eType )
519 {
520 case typeNone:
521 reset( rhs.eType );
522 if( eType == typeString )
523 {
524 uVal.sVal = new Bu::FString( *rhs.uVal.sVal );
525 }
526 else if( eType == typeList )
527 {
528 uVal.lVal = new VarList( *rhs.uVal.lVal );
529 }
530 else
531 {
532 uVal = rhs.uVal;
533 }
534 break;
535
536 case typeString:
537 uVal.sVal->append( rhs.getString() );
538 break;
539
540 case typeList:
541 uVal.lVal->append( rhs.getList() );
542 break;
543
544 default:
545 throw Bu::ExceptionBase("Can't << with non-string or non-list.");
546 break;
547 }
548 return *this;
549}
550
551bool Variable::operator==( const Variable &rhs ) const
552{
553 if( eType != rhs.eType )
554 return false;
555 switch( eType )
556 {
557 case typeNone:
558 return true;
559
560 case typeInt:
561 return uVal.iVal == rhs.uVal.iVal;
562
563 case typeFloat:
564 return uVal.fVal == rhs.uVal.fVal;
565
566 case typeBool:
567 return uVal.bVal == rhs.uVal.bVal;
568
569 case typeString:
570 case typeRef:
571 return *uVal.sVal == *rhs.uVal.sVal;
572
573 case typeList:
574 return *uVal.lVal == *rhs.uVal.lVal;
575
576 case typeVersion:
577 return false;
578 }
579
580 return false;
581}
582
583bool Variable::operator!=( const Variable &rhs ) const
584{
585 return !(*this == rhs);
586}
587
588bool Variable::operator<( const Variable &rhs ) const
589{
590 Type eTop = Bu::max( eType, rhs.eType );
591 switch( eTop )
592 {
593 case typeNone:
594 return false;
595
596 case typeBool:
597 throw Bu::ExceptionBase("You cannot < compare boolean values.");
598
599 case typeInt:
600 return toInt() < rhs.toInt();
601
602 case typeFloat:
603 return toFloat() < rhs.toFloat();
604
605 case typeVersion:
606 return true;
607
608 case typeString:
609 return toString() < rhs.toString();
610
611 case typeList:
612 throw Bu::ExceptionBase("You cannot < compare list values.");
613
614 case typeRef:
615 throw Bu::ExceptionBase("You cannot < compare reference values.");
616 }
617 throw Bu::ExceptionBase("Unhandled case in Variable < compare");
618}
619
620bool Variable::operator>( const Variable &rhs ) const
621{
622 Type eTop = Bu::max( eType, rhs.eType );
623 switch( eTop )
624 {
625 case typeNone:
626 return false;
627
628 case typeBool:
629 throw Bu::ExceptionBase("You cannot > compare boolean values.");
630
631 case typeInt:
632 return toInt() > rhs.toInt();
633
634 case typeFloat:
635 return toFloat() > rhs.toFloat();
636
637 case typeVersion:
638 return true;
639
640 case typeString:
641 return toString() > rhs.toString();
642
643 case typeList:
644 throw Bu::ExceptionBase("You cannot > compare list values.");
645
646 case typeRef:
647 throw Bu::ExceptionBase("You cannot > compare reference values.");
648 }
649 throw Bu::ExceptionBase("Unhandled case in Variable > compare");
650}
651
652bool Variable::operator<=( const Variable &rhs ) const
653{
654 Type eTop = Bu::max( eType, rhs.eType );
655 switch( eTop )
656 {
657 case typeNone:
658 return false;
659
660 case typeBool:
661 throw Bu::ExceptionBase("You cannot <= compare boolean values.");
662
663 case typeInt:
664 return toInt() <= rhs.toInt();
665
666 case typeFloat:
667 return toFloat() <= rhs.toFloat();
668
669 case typeVersion:
670 return true;
671
672 case typeString:
673 return toString() <= rhs.toString();
674
675 case typeList:
676 throw Bu::ExceptionBase("You cannot <= compare list values.");
677
678 case typeRef:
679 throw Bu::ExceptionBase("You cannot <= compare reference values.");
680 }
681 throw Bu::ExceptionBase("Unhandled case in Variable <= compare");
682}
683
684bool Variable::operator>=( const Variable &rhs ) const
685{
686 Type eTop = Bu::max( eType, rhs.eType );
687 switch( eTop )
688 {
689 case typeNone:
690 return false;
691
692 case typeBool:
693 throw Bu::ExceptionBase("You cannot >= compare boolean values.");
694
695 case typeInt:
696 return toInt() >= rhs.toInt();
697
698 case typeFloat:
699 return toFloat() >= rhs.toFloat();
700
701 case typeVersion:
702 return true;
703
704 case typeString:
705 return toString() >= rhs.toString();
706
707 case typeList:
708 throw Bu::ExceptionBase("You cannot >= compare list values.");
709
710 case typeRef:
711 throw Bu::ExceptionBase("You cannot >= compare reference values.");
712 }
713 throw Bu::ExceptionBase("Unhandled case in Variable >= compare");
714}
715
716Variable Variable::operator+( const Variable &rhs ) const
717{
718 Type eTop = Bu::max( eType, rhs.eType );
719 switch( eTop )
720 {
721 case typeNone:
722 return Variable();
723
724 case typeBool:
725 throw Bu::ExceptionBase("You cannot add boolean values.");
726
727 case typeInt:
728 return Variable( toInt() + rhs.toInt() );
729
730 case typeFloat:
731 return Variable( toFloat() + rhs.toFloat() );
732
733 case typeVersion:
734 throw Bu::ExceptionBase("You cannot add version values.");
735
736 case typeString:
737 return Variable( toString() + rhs.toString() );
738
739 case typeList:
740 return Variable( toList() + rhs.toList() );
741
742 case typeRef:
743 throw Bu::ExceptionBase("You cannot add reference values.");
744 }
745 throw Bu::ExceptionBase("Unhandled case in Variable add");
746}
747
748Variable Variable::operator-( const Variable &rhs ) const
749{
750 Type eTop = Bu::max( eType, rhs.eType );
751 switch( eTop )
752 {
753 case typeNone:
754 return Variable();
755
756 case typeBool:
757 throw Bu::ExceptionBase("You cannot subtract boolean values.");
758
759 case typeInt:
760 return Variable( toInt() - rhs.toInt() );
761
762 case typeFloat:
763 return Variable( toFloat() - rhs.toFloat() );
764
765 case typeVersion:
766 throw Bu::ExceptionBase("You cannot subtract version values.");
767
768 case typeString:
769 throw Bu::ExceptionBase("You cannot subtract string values.");
770
771 case typeList:
772 throw Bu::ExceptionBase("You cannot subtract list values.");
773
774 case typeRef:
775 throw Bu::ExceptionBase("You cannot subtract reference values.");
776 }
777 throw Bu::ExceptionBase("Unhandled case in Variable subtract");
778}
779
780Variable Variable::operator*( const Variable &rhs ) const
781{
782 Type eTop = Bu::max( eType, rhs.eType );
783 switch( eTop )
784 {
785 case typeNone:
786 return Variable();
787
788 case typeBool:
789 throw Bu::ExceptionBase("You cannot multiply boolean values.");
790
791 case typeInt:
792 return Variable( toInt() * rhs.toInt() );
793
794 case typeFloat:
795 return Variable( toFloat() * rhs.toFloat() );
796
797 case typeVersion:
798 throw Bu::ExceptionBase("You cannot multiply version values.");
799
800 case typeString:
801 throw Bu::ExceptionBase("You cannot multiply string values.");
802
803 case typeList:
804 throw Bu::ExceptionBase("You cannot multiply list values.");
805
806 case typeRef:
807 throw Bu::ExceptionBase("You cannot multiply reference values.");
808 }
809 throw Bu::ExceptionBase("Unhandled case in Variable multiply");
810}
811
812Variable Variable::operator/( const Variable &rhs ) const
813{
814 Type eTop = Bu::max( eType, rhs.eType );
815 switch( eTop )
816 {
817 case typeNone:
818 return Variable();
819
820 case typeBool:
821 throw Bu::ExceptionBase("You cannot divide boolean values.");
822
823 case typeInt:
824 return Variable( toInt() / rhs.toInt() );
825
826 case typeFloat:
827 return Variable( toFloat() / rhs.toFloat() );
828
829 case typeVersion:
830 throw Bu::ExceptionBase("You cannot divide version values.");
831
832 case typeString:
833 throw Bu::ExceptionBase("You cannot divide string values.");
834
835 case typeList:
836 throw Bu::ExceptionBase("You cannot divide list values.");
837
838 case typeRef:
839 throw Bu::ExceptionBase("You cannot divide reference values.");
840 }
841 throw Bu::ExceptionBase("Unhandled case in Variable divide");
842}
843
844void Variable::reset( Type eNewType )
845{
846 if( eType == typeString || eType == typeRef )
847 {
848 delete uVal.sVal;
849 }
850 else if( eType == typeList )
851 {
852 delete uVal.lVal;
853 }
854 memset( &uVal, 0, sizeof(uVal) );
855
856 eType = eNewType;
857}
858
859Bu::Formatter &operator<<( Bu::Formatter &f, const Variable::Type &t )
860{
861 switch( t )
862 {
863 case Variable::typeNone: f << "*typeless*"; break;
864 case Variable::typeInt: f << "int"; break;
865 case Variable::typeFloat: f << "double"; break;
866 case Variable::typeBool: f << "bool"; break;
867 case Variable::typeString: f << "string"; break;
868 case Variable::typeList: f << "list"; break;
869 case Variable::typeVersion: f << "version"; break;
870 case Variable::typeRef: f << "ref"; break;
871 }
872 return f;
873}
874
875Bu::Formatter &operator<<( Bu::Formatter &f, const Variable &v )
876{
877 f << "Variable(" << v.getType() << ") = ";
878 switch( v.getType() )
879 {
880 case Variable::typeNone: break;
881 case Variable::typeInt: f << v.getInt(); break;
882 case Variable::typeFloat: f << v.getFloat(); break;
883 case Variable::typeBool: f << v.getBool(); break;
884 case Variable::typeString: f << v.getString(); break;
885 case Variable::typeList: f << v.getList(); break;
886 case Variable::typeVersion:/*f << v.getVersion();*/ break;
887 case Variable::typeRef: f << v.getString(); break;
888 }
889
890 return f;
891}
892
diff --git a/src/variable.h b/src/variable.h
new file mode 100644
index 0000000..1b5542e
--- /dev/null
+++ b/src/variable.h
@@ -0,0 +1,115 @@
1#ifndef VARIABLE_H
2#define VARIABLE_H
3
4#include "types.h"
5
6/**
7 * A build variable, which is basically a flexible, limited type range variant.
8 */
9class Variable
10{
11public:
12 enum Type
13 {
14 typeNone,
15 typeBool,
16 typeInt,
17 typeFloat,
18 typeVersion,
19 typeString,
20 typeList,
21 typeRef /**< Reference by name, it's just a string. */
22 };
23
24public:
25 Variable();
26 Variable( Type t );
27 Variable( int iVal );
28 Variable( double fVal );
29 Variable( bool bVal );
30 Variable( const Bu::FString &sVal );
31 Variable( const char *sVal );
32 Variable( const Variable &v );
33 Variable( const class AstLeaf &l );
34 /**
35 * This special case function turns the variable into a string if there is
36 * only one string in the list, or a list of strings if there is more or
37 * less than one.
38 */
39 Variable( const StrList &lst );
40 Variable( const VarList &lst );
41 virtual ~Variable();
42
43 static Variable mkRef( const Bu::FString &sVal );
44
45 Type getType() const;
46
47 // Raw aquisition functions, if the type isn't right,
48 // they throw an exception
49 int getInt() const;
50 double getFloat() const;
51 bool getBool() const;
52 const Bu::FString &getString() const;
53 const VarList &getList() const;
54
55 // Conversion functions, they'll return the requested type, maybe an error
56 // if the source data is really bad
57 int toInt() const;
58 double toFloat() const;
59 bool toBool() const;
60 Bu::FString toString() const;
61 VarList toList() const;
62
63 Variable toType( Type eNewType ) const;
64
65 void append( const Variable &v );
66 VarList::iterator begin();
67 VarList::const_iterator begin() const;
68
69 void doNegate();
70 void doNot();
71
72 const Variable &operator=( const Variable &rhs );
73 const Variable &operator=( const int &rhs );
74 const Variable &operator=( const double &rhs );
75 const Variable &operator=( const bool &rhs );
76 const Variable &operator=( const Bu::FString &rhs );
77
78 const Variable &operator+=( const Variable &rhs );
79 const Variable &operator<<( const Variable &rhs );
80
81 bool operator==( const Variable &rhs ) const;
82 bool operator!=( const Variable &rhs ) const;
83 bool operator<( const Variable &rhs ) const;
84 bool operator>( const Variable &rhs ) const;
85 bool operator<=( const Variable &rhs ) const;
86 bool operator>=( const Variable &rhs ) const;
87
88 Variable operator+( const Variable &rhs ) const;
89 Variable operator-( const Variable &rhs ) const;
90 Variable operator*( const Variable &rhs ) const;
91 Variable operator/( const Variable &rhs ) const;
92
93private:
94 Type eType;
95 union
96 {
97 int iVal;
98 double fVal;
99 bool bVal;
100 Bu::FString *sVal;
101 VarList *lVal;
102 } uVal;
103
104 void reset( Type eType );
105};
106
107namespace Bu
108{
109 class Formatter;
110}
111
112Bu::Formatter &operator<<( Bu::Formatter &f, const Variable::Type &t );
113Bu::Formatter &operator<<( Bu::Formatter &f, const Variable &v );
114
115#endif
diff --git a/src/view.cpp b/src/view.cpp
new file mode 100644
index 0000000..6dcc9bb
--- /dev/null
+++ b/src/view.cpp
@@ -0,0 +1,10 @@
1#include "view.h"
2
3View::View()
4{
5}
6
7View::~View()
8{
9}
10
diff --git a/src/view.h b/src/view.h
new file mode 100644
index 0000000..fb172aa
--- /dev/null
+++ b/src/view.h
@@ -0,0 +1,46 @@
1#ifndef VIEW_H
2#define VIEW_H
3
4#include <bu/fstring.h>
5#include "types.h"
6
7/**
8 * Base class for all views. A view is the only way that build is allowed to
9 * communicate with the user during the processing of a buildfile, the main
10 * executable may output some things for command line arguments and whatnot on
11 * it's own, or debugging info, but all reports of everything happening during
12 * the process is sent through a view, so it can be made pretty and usable.
13 */
14class View
15{
16public:
17 View();
18 virtual ~View();
19
20 virtual void beginAction( const Bu::FString &sAction )=0;
21 virtual void endAction()=0;
22
23 virtual void skipTarget( const Bu::FString &sProfile,
24 const Target &rTarget )=0;
25 virtual void beginTarget( const Bu::FString &sProfile,
26 const Target &rTarget )=0;
27 virtual void processTarget( const Bu::FString &sProfile,
28 const Target &rTarget )=0;
29 virtual void endTarget()=0;
30
31 virtual void buildRequires( const Target &rTarget )=0;
32 virtual void cmdStarted( const Bu::FString &sCmd )=0;
33 virtual void cmdFinished( const Bu::FString &sStdOut,
34 const Bu::FString &sStdErr, long iExit )=0;
35
36 virtual void userError( const Bu::FString &sMsg )=0;
37 virtual void userWarning( const Bu::FString &sMsg )=0;
38 virtual void userNotice( const Bu::FString &sMsg )=0;
39
40 virtual void sysError( const Bu::FString &sMsg )=0;
41 virtual void sysWarning( const Bu::FString &sMsg )=0;
42
43private:
44};
45
46#endif
diff --git a/src/viewdefault.cpp b/src/viewdefault.cpp
new file mode 100644
index 0000000..c38c62f
--- /dev/null
+++ b/src/viewdefault.cpp
@@ -0,0 +1,170 @@
1#include "viewdefault.h"
2#include "target.h"
3
4#include <bu/plugger.h>
5
6#include <bu/sio.h>
7using namespace Bu;
8
9PluginInterface3( pluginViewDefault, default, ViewDefault, View,
10 "Mike Buland", 0, 1 );
11
12#define ESC "\x1b"
13
14#define C_RESET ESC "[0m"
15#define C_RED ESC "[31m"
16#define C_GREEN ESC "[32m"
17#define C_YELLOW ESC "[33m"
18#define C_BLUE ESC "[34m"
19#define C_MAGENTA ESC "[35m"
20#define C_CYAN ESC "[36m"
21#define C_WHITE ESC "[37m"
22#define C_DEFAULT ESC "[39m"
23
24#define C_BR_RED ESC "[1;31m"
25#define C_BR_GREEN ESC "[1;32m"
26#define C_BR_YELLOW ESC "[1;33m"
27#define C_BR_BLUE ESC "[1;34m"
28#define C_BR_MAGENTA ESC "[1;35m"
29#define C_BR_CYAN ESC "[1;36m"
30#define C_BR_WHITE ESC "[1;37m"
31
32ViewDefault::ViewDefault() :
33 bFirst( true ),
34 iDepth( 0 ),
35 iTotal( 0 ),
36 iCurrent( 0 )
37{
38}
39
40ViewDefault::~ViewDefault()
41{
42}
43
44void ViewDefault::beginAction( const Bu::FString &/*sAction*/ )
45{
46}
47
48void ViewDefault::endAction()
49{
50}
51
52void ViewDefault::skipTarget( const Bu::FString &/*sProfile*/,
53 const Target &/*rTarget*/ )
54{
55 iCurrent++;
56}
57
58void ViewDefault::beginTarget( const Bu::FString &sProfile,
59 const Target &rTarget )
60{
61 if( iDepth == 0 )
62 {
63 iTotal = rTarget.getDepCount();
64 iCurrent = 0;
65 if( bFirst == false )
66 {
67 sio << sio.nl;
68 }
69 bFirst = false;
70 sio << C_BR_WHITE << " --- " << C_BR_CYAN << sProfile << " "
71 << rTarget.getOutputList().first() << C_BR_WHITE << " --- "
72 << C_RESET << sio.nl;
73 }
74 iDepth++;
75}
76
77void ViewDefault::processTarget( const Bu::FString &/*sProfile*/,
78 const Target &rTarget )
79{
80 iCurrent++;
81
82 int iPct = (iTotal>0)?(iCurrent*100/iTotal):(100);
83 sio << C_BR_WHITE << "[" << C_BR_GREEN << Fmt(3) << iPct
84 << "%" << C_BR_WHITE << "] " << C_BR_MAGENTA
85 << Fmt(10) << rTarget.getDisplay() << C_BR_WHITE
86 << ": " << rTarget.getOutputList().first() << C_RESET << sio.nl;
87}
88
89void ViewDefault::endTarget()
90{
91 iDepth--;
92}
93
94void ViewDefault::buildRequires( const Target &rTarget )
95{
96 int iPct = (iTotal>0)?(iCurrent*100/iTotal):(100);
97 sio << C_BR_WHITE << "[" << C_BR_GREEN << Fmt(3) << iPct
98 << "%" << C_BR_WHITE << "] " << C_BR_MAGENTA
99 << Fmt(10) << "deps" << C_BR_WHITE
100 << ": " << rTarget.getOutputList().first() << C_RESET << sio.nl;
101}
102
103void ViewDefault::cmdStarted( const Bu::FString &/*sCmd*/ )
104{
105}
106
107void ViewDefault::cmdFinished( const Bu::FString &sStdOut,
108 const Bu::FString &sStdErr, long /*iExit*/ )
109{
110 if( sStdOut )
111 {
112 Bu::FString::const_iterator b;
113 b = sStdOut.begin();
114 while( b )
115 {
116 Bu::FString::const_iterator e, max;
117 max = b + 78;
118 for( e = b; e != max && *e != '\n'; e++ ) { }
119 sio << C_BR_GREEN << "| " << C_RESET << FString( b, e ) << sio.nl;
120 b = e;
121 if( *b == '\n' )
122 b++;
123 }
124 sio << C_BR_GREEN << "\\-----" << C_RESET << sio.nl;
125 }
126 if( sStdErr )
127 {
128 Bu::FString::const_iterator b;
129 b = sStdErr.begin();
130 while( b )
131 {
132 Bu::FString::const_iterator e, max;
133 max = b + 78;
134 for( e = b; e != max && *e != '\n'; e++ ) { }
135 sio << C_BR_RED << "| " << C_RESET << FString( b, e ) << sio.nl;
136 b = e;
137 if( *b == '\n' )
138 b++;
139 }
140 sio << C_BR_RED << "\\-----" << C_RESET << sio.nl;
141 }
142 //sio << C_BR_WHITE << "[" << C_BR_GREEN << sStdOut << C_BR_WHITE << "]" << sio.nl;
143 //sio << C_BR_WHITE << "[" << C_BR_RED << sStdErr << C_BR_WHITE << "]" << sio.nl;
144}
145
146void ViewDefault::userError( const Bu::FString &sMsg )
147{
148 sio << C_BR_RED << "Error: " << sMsg << C_RESET << sio.nl;
149}
150
151void ViewDefault::userWarning( const Bu::FString &sMsg )
152{
153 sio << C_BR_YELLOW << "Warning: " << sMsg << C_RESET << sio.nl;
154}
155
156void ViewDefault::userNotice( const Bu::FString &sMsg )
157{
158 sio << C_BR_GREEN << "Notice: " << sMsg << C_RESET << sio.nl;
159}
160
161void ViewDefault::sysError( const Bu::FString &sMsg )
162{
163 sio << C_BR_RED << sMsg << C_RESET << sio.nl;
164}
165
166void ViewDefault::sysWarning( const Bu::FString &sMsg )
167{
168 sio << C_BR_YELLOW << sMsg << C_RESET << sio.nl;
169}
170
diff --git a/src/viewdefault.h b/src/viewdefault.h
new file mode 100644
index 0000000..c9a554d
--- /dev/null
+++ b/src/viewdefault.h
@@ -0,0 +1,42 @@
1#ifndef VIEW_DEFAULT_H
2#define VIEW_DEFAULT_H
3
4#include "view.h"
5
6class ViewDefault : public View
7{
8public:
9 ViewDefault();
10 virtual ~ViewDefault();
11
12 virtual void beginAction( const Bu::FString &sAction );
13 virtual void endAction();
14
15 virtual void skipTarget( const Bu::FString &sProfile,
16 const Target &rTarget );
17 virtual void beginTarget( const Bu::FString &sProfile,
18 const Target &rTarget );
19 virtual void processTarget( const Bu::FString &sProfile,
20 const Target &rTarget );
21 virtual void endTarget();
22
23 virtual void buildRequires( const Target &rTarget );
24 virtual void cmdStarted( const Bu::FString &sCmd );
25 virtual void cmdFinished( const Bu::FString &sStdOut,
26 const Bu::FString &sStdErr, long iExit );
27
28 virtual void userError( const Bu::FString &sMsg );
29 virtual void userWarning( const Bu::FString &sMsg );
30 virtual void userNotice( const Bu::FString &sMsg );
31
32 virtual void sysError( const Bu::FString &sMsg );
33 virtual void sysWarning( const Bu::FString &sMsg );
34
35private:
36 bool bFirst;
37 int iDepth;
38 int iTotal;
39 int iCurrent;
40};
41
42#endif
diff --git a/src/viewmake.cpp b/src/viewmake.cpp
new file mode 100644
index 0000000..39d1327
--- /dev/null
+++ b/src/viewmake.cpp
@@ -0,0 +1,86 @@
1#include "viewmake.h"
2#include "target.h"
3
4#include <bu/plugger.h>
5
6#include <bu/sio.h>
7using namespace Bu;
8
9PluginInterface3( pluginViewMake, make, ViewMake, View,
10 "Mike Buland", 0, 1 );
11
12ViewMake::ViewMake()
13{
14}
15
16ViewMake::~ViewMake()
17{
18}
19
20void ViewMake::beginAction( const Bu::FString &/*sAction*/ )
21{
22}
23
24void ViewMake::endAction()
25{
26}
27
28void ViewMake::skipTarget( const Bu::FString &/*sProfile*/,
29 const Target &/*rTarget*/ )
30{
31}
32
33void ViewMake::beginTarget( const Bu::FString &/*sProfile*/,
34 const Target &/*rTarget*/ )
35{
36}
37
38void ViewMake::processTarget( const Bu::FString &/*sProfile*/,
39 const Target &/*rTarget*/ )
40{
41}
42
43void ViewMake::endTarget()
44{
45}
46
47void ViewMake::buildRequires( const Target &/*rTarget*/ )
48{
49}
50
51void ViewMake::cmdStarted( const Bu::FString &sCmd )
52{
53 sio << sCmd << sio.nl;
54}
55
56void ViewMake::cmdFinished( const Bu::FString &sStdOut,
57 const Bu::FString &sStdErr, long /*iExit*/ )
58{
59 sio << sStdOut << sStdErr;
60}
61
62void ViewMake::userError( const Bu::FString &sMsg )
63{
64 sio << "Error: " << sMsg << sio.nl;
65}
66
67void ViewMake::userWarning( const Bu::FString &sMsg )
68{
69 sio << "Warning: " << sMsg << sio.nl;
70}
71
72void ViewMake::userNotice( const Bu::FString &sMsg )
73{
74 sio << "Notice: " << sMsg << sio.nl;
75}
76
77void ViewMake::sysError( const Bu::FString &sMsg )
78{
79 sio << sMsg << sio.nl;
80}
81
82void ViewMake::sysWarning( const Bu::FString &sMsg )
83{
84 sio << sMsg << sio.nl;
85}
86
diff --git a/src/viewmake.h b/src/viewmake.h
new file mode 100644
index 0000000..9100a86
--- /dev/null
+++ b/src/viewmake.h
@@ -0,0 +1,36 @@
1#ifndef VIEW_MAKE_H
2#define VIEW_MAKE_H
3
4#include "view.h"
5
6class ViewMake : public View
7{
8public:
9 ViewMake();
10 virtual ~ViewMake();
11
12 virtual void beginAction( const Bu::FString &sAction );
13 virtual void endAction();
14
15 virtual void skipTarget( const Bu::FString &sProfile,
16 const Target &rTarget );
17 virtual void beginTarget( const Bu::FString &sProfile,
18 const Target &rTarget );
19 virtual void processTarget( const Bu::FString &sProfile,
20 const Target &rTarget );
21 virtual void endTarget();
22
23 virtual void buildRequires( const Target &rTarget );
24 virtual void cmdStarted( const Bu::FString &sCmd );
25 virtual void cmdFinished( const Bu::FString &sStdOut,
26 const Bu::FString &sStdErr, long iExit );
27
28 virtual void userError( const Bu::FString &sMsg );
29 virtual void userWarning( const Bu::FString &sMsg );
30 virtual void userNotice( const Bu::FString &sMsg );
31
32 virtual void sysError( const Bu::FString &sMsg );
33 virtual void sysWarning( const Bu::FString &sMsg );
34};
35
36#endif
diff --git a/src/viewplugger.cpp b/src/viewplugger.cpp
new file mode 100644
index 0000000..6046f82
--- /dev/null
+++ b/src/viewplugger.cpp
@@ -0,0 +1,14 @@
1#include "viewplugger.h"
2
3extern Bu::PluginInfo pluginViewDefault;
4extern Bu::PluginInfo pluginViewMake;
5ViewPlugger::ViewPlugger()
6{
7 registerBuiltinPlugin( &pluginViewDefault );
8 registerBuiltinPlugin( &pluginViewMake );
9}
10
11ViewPlugger::~ViewPlugger()
12{
13}
14
diff --git a/src/viewplugger.h b/src/viewplugger.h
new file mode 100644
index 0000000..b58635c
--- /dev/null
+++ b/src/viewplugger.h
@@ -0,0 +1,20 @@
1#ifndef VIEW_PLUGGER_H
2#define VIEW_PLUGGER_H
3
4#include "view.h"
5#include <bu/plugger.h>
6#include <bu/singleton.h>
7
8class ViewPlugger : public Bu::Plugger<View>, public Bu::Singleton<ViewPlugger>
9{
10 friend class Bu::Singleton<ViewPlugger>;
11private:
12 ViewPlugger();
13
14public:
15 virtual ~ViewPlugger();
16
17private:
18};
19
20#endif
diff --git a/support/vim/ftdetect/build.vim b/support/vim/ftdetect/build.vim
new file mode 100644
index 0000000..8419934
--- /dev/null
+++ b/support/vim/ftdetect/build.vim
@@ -0,0 +1 @@
au BufRead,BufNewFile *.bld set filetype=build
diff --git a/support/vim/ftplugin/build.vim b/support/vim/ftplugin/build.vim
new file mode 100644
index 0000000..6b24838
--- /dev/null
+++ b/support/vim/ftplugin/build.vim
@@ -0,0 +1,24 @@
1" Vim filetype plugin file
2" Language: Build
3
4" Only do this when not done yet for this buffer
5if exists("b:did_ftplugin")
6 finish
7endif
8
9" Don't load another plugin for this buffer
10let b:did_ftplugin = 1
11
12" Set format options -- allow comment formatting with gq, but disable
13" other processing
14setlocal fo-=tcrowan2 fo+=q
15
16" Set 'comments' to be the same as C/C++
17setlocal comments=sO:*\ -,mO:*\ \ ,exO:*/,s1:/*,mb:*,ex:*/,://
18
19" Win32 can filter files in the browse dialog
20if has("gui_win32") && !exists("b:browsefilter")
21 let b:browsefilter = "Build files (*.bld)\t*.bld\n" .
22 \ "All Files (*.*)\t*.*\n"
23endif
24
diff --git a/support/vim/syntax/build.vim b/support/vim/syntax/build.vim
new file mode 100644
index 0000000..ae83d94
--- /dev/null
+++ b/support/vim/syntax/build.vim
@@ -0,0 +1,70 @@
1" Vim syntax file
2" Language: Buildscript
3" Maintainer: Mike Buland :)
4" Last Change: 2009 Dec 4
5
6" For version 5.x: Clear all syntax items
7" For version 6.x: Quit when a syntax file was already loaded
8if version < 600
9 syntax clear
10elseif exists("b:current_syntax")
11 finish
12endif
13
14" Keywords
15syn keyword Conditional if then else
16syn keyword Loop for do in
17syn keyword Logic not and or
18syn keyword Statement include set unset function target input condition requires rule profile auto config display type default cache global value return output allow action warning error notice local continue break all export tag
19syn keyword Todo TODO FIXME XXX
20syn keyword Type int string bool float version
21syn keyword Constant null true false file never always important normal hidden autogenerated filetime
22syn keyword Builtins files dirs matches replace regexp execute unlink exists getMakeDeps toString targets fileName dirName
23
24syn match TargetProcess /[a-zA-Z_][a-zA-Z0-9_]*:/he=e-1
25
26syn cluster CommentGroup contains=Todo
27
28syn match Special display contained "\\\(x\x\+\|\o\{1,3}\|.\|$\)"
29syn match Special display contained "\\\(u\x\{4}\|U\x\{8}\)"
30
31" TODO: do we want to end at end of line too?
32syn region doubleString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=Special,CmdEx
33
34syn region CmdEx start=+$(+ skip=+"\|\\)\|\\\\+ end=+)+
35
36syn case ignore
37syn match Numbers display transparent "\<\d\|\.\d" contains=Number,Float,OctalError,Octal
38syn match Number display contained "\d\+\(u\=l\{0,2}\|ll\=u\)\>"
39" Hex
40syn match Number display contained "0x\x\+\(u\=l\{0,2}\|ll\=u\)\>"
41syn match Octal display contained "0\o\+\(u\=l\{0,2}\|ll\=u\)\>" contains=cOctalZero
42syn match OctalZero display contained "\<0"
43syn match Float display contained "\d\+f"
44syn match Float display contained "\d\+\.\d*\(e[-+]\=\d\+\)\=[fl]\="
45syn match Float display contained "\.\d\+\(e[-+]\=\d\+\)\=[fl]\=\>"
46syn match Float display contained "\d\+e[-+]\=\d\+[fl]\=\>"
47" Flag bad digits in octal
48syn match OctalError display contained "0\o*[89]\d*"
49syn case match
50
51
52syn region cppComment start="#" skip="\\$" end="$" contains=@CommentGroup keepend
53syn region cComment start="/\*" end="\*/" contains=@CommentGroup fold
54syn region cppComment start="//" skip="\\$" end="$" contains=@CommentGroup keepend
55
56
57syntax region Block start="{" end="}" transparent fold
58
59hi def link OctalError Error
60hi def link cComment Comment
61hi def link cppComment Comment
62hi def link singleString String
63hi def link doubleString String
64hi def link cmdEx String
65hi def link Constructor Operator
66hi def link Logic Statement
67hi def link Loop Conditional
68hi def link Builtins Function
69hi def link TargetProcess Type
70