aboutsummaryrefslogtreecommitdiff
path: root/tools/mhmake/src
diff options
context:
space:
mode:
authormarha <marha@users.sourceforge.net>2009-07-29 09:17:47 +0000
committermarha <marha@users.sourceforge.net>2009-07-29 09:17:47 +0000
commit437bca524595d1007f37988e862db8bfeff329b0 (patch)
treeda106b22babce6ccd53c7dd9c3040aed4a52f455 /tools/mhmake/src
parent6f45caf0fe3e94a83835fa1f5d04233b9e936efc (diff)
downloadvcxsrv-437bca524595d1007f37988e862db8bfeff329b0.tar.gz
vcxsrv-437bca524595d1007f37988e862db8bfeff329b0.tar.bz2
vcxsrv-437bca524595d1007f37988e862db8bfeff329b0.zip
Added mhmake GNU make compatible (with extensions) make utility.
Diffstat (limited to 'tools/mhmake/src')
-rw-r--r--tools/mhmake/src/Makefile.am61
-rw-r--r--tools/mhmake/src/bison.cc1028
-rw-r--r--tools/mhmake/src/bison.h252
-rw-r--r--tools/mhmake/src/build.cpp1703
-rw-r--r--tools/mhmake/src/curdir.cpp61
-rw-r--r--tools/mhmake/src/curdir.h49
-rw-r--r--tools/mhmake/src/fileinfo.cpp339
-rw-r--r--tools/mhmake/src/fileinfo.h450
-rw-r--r--tools/mhmake/src/flexskel.cc1056
-rw-r--r--tools/mhmake/src/flexskel.h395
-rw-r--r--tools/mhmake/src/functions.cpp767
-rw-r--r--tools/mhmake/src/md5.cpp456
-rw-r--r--tools/mhmake/src/md5.h58
-rw-r--r--tools/mhmake/src/mhmake.cpp132
-rw-r--r--tools/mhmake/src/mhmakefileparser.cpp1086
-rw-r--r--tools/mhmake/src/mhmakefileparser.h279
-rw-r--r--tools/mhmake/src/mhmakelexer.l910
-rw-r--r--tools/mhmake/src/mhmakeparser.y224
-rw-r--r--tools/mhmake/src/refptr.h124
-rw-r--r--tools/mhmake/src/rule.cpp242
-rw-r--r--tools/mhmake/src/rule.h89
-rw-r--r--tools/mhmake/src/stdafx.cpp24
-rw-r--r--tools/mhmake/src/stdafx.h83
-rw-r--r--tools/mhmake/src/util.cpp708
-rw-r--r--tools/mhmake/src/util.h229
25 files changed, 10805 insertions, 0 deletions
diff --git a/tools/mhmake/src/Makefile.am b/tools/mhmake/src/Makefile.am
new file mode 100644
index 000000000..9e84d0f1f
--- /dev/null
+++ b/tools/mhmake/src/Makefile.am
@@ -0,0 +1,61 @@
+SRCS = mhmakeparser.y mhmakelexer.l mhmake.cpp mhmakefileparser.cpp util.cpp \
+ functions.cpp fileinfo.cpp rule.cpp md5.c build.cpp curdir.cpp
+
+if DEBUG
+bin_PROGRAMS=mhmake_dbg
+mhmake_dbg_SOURCES = $(SRCS)
+else
+bin_PROGRAMS=mhmake
+mhmake_SOURCES = $(SRCS)
+endif
+
+LEX=flex++
+AM_LFLAGS=-8
+
+YACC=bison++
+AM_YFLAGS=-d
+
+mhmakelexer.o: mhmakelexer.cpp mhmakelexer.h
+mhmakelexer.cpp: mhmakelexer.l
+mhmakelexer.h: mhmakelexer.l
+
+mhmakeparser.o: mhmakeparser.cpp mhmakeparser.h
+mhmakeparser.cpp: mhmakeparser.y
+mhmakeparser.h: mhmakeparser.y
+
+.l.cpp:
+ $(LEXCOMPILE) -S$(dir $<)flexskel.cc -H$(dir $<)flexskel.h -h$(@:%.cpp=%.h) -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $@
+ cat temp1234.456 >> $@
+ rm temp1234.456
+
+.y.cpp:
+ $(YACCCOMPILE) -S$(dir $<)bison.cc -H$(dir $<)bison.h -h$(@:%.cpp=%.h) -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $@
+ cat temp1234.456 >> $@
+ rm temp1234.456
+
+
+.l.h:
+ $(LEXCOMPILE) -S$(dir $<)flexskel.cc -H$(dir $<)flexskel.h -h$@ -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $(@:%.h=%.cpp)
+ cat temp1234.456 >> $(@:%.h=%.cpp)
+ rm temp1234.456
+
+.y.h:
+ $(YACCCOMPILE) -S$(dir $<)bison.cc -H$(dir $<)bison.h -h$@ -otemp1234.456 $<
+ echo '#include "stdafx.h"' > $(@:%.h=%.cpp)
+ cat temp1234.456 >> $(@:%.h=%.cpp)
+ rm temp1234.456
+
+# set the include path found by configure
+INCLUDES= $(all_includes)
+
+LDADD = /usr/lib/libpopt.a
+
+# the library search path.
+if DEBUG
+mhmake_dbg_LDFLAGS = $(all_libraries)
+else
+mhmake_LDFLAGS = $(all_libraries)
+endif
diff --git a/tools/mhmake/src/bison.cc b/tools/mhmake/src/bison.cc
new file mode 100644
index 000000000..37596b561
--- /dev/null
+++ b/tools/mhmake/src/bison.cc
@@ -0,0 +1,1028 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* -*-C-*- Note some compilers choke on comments on `#line' lines. */
+/* Skeleton output parser for bison,
+ Copyright (C) 1984, 1989, 1990 Bob Corbett and Richard Stallman
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 1, or (at your option)
+ any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+/* HEADER SECTION */
+#if defined( _MSDOS ) || defined(MSDOS) || defined(__MSDOS__)
+#define __MSDOS_AND_ALIKE
+#endif
+#if defined(_WINDOWS) && defined(_MSC_VER)
+#define __HAVE_NO_ALLOCA
+#define __MSDOS_AND_ALIKE
+#endif
+
+#ifndef alloca
+#if defined( __GNUC__)
+#define alloca __builtin_alloca
+
+#elif (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi)
+#include <alloca.h>
+
+#elif defined (__MSDOS_AND_ALIKE)
+#include <malloc.h>
+#ifndef __TURBOC__
+/* MS C runtime lib */
+#define alloca _alloca
+#endif
+
+#elif defined(_AIX)
+#include <malloc.h>
+#pragma alloca
+
+#elif defined(__hpux)
+#ifdef __cplusplus
+extern "C" {
+void *alloca (unsigned int);
+};
+#else /* not __cplusplus */
+void *alloca ();
+#endif /* not __cplusplus */
+
+#endif /* not _AIX not MSDOS, or __TURBOC__ or _AIX, not sparc. */
+#endif /* alloca not defined. */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+#ifdef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_USE_CLASS
+#endif
+#else
+#ifndef __STDC__
+#define const
+#endif
+#endif
+#include <stdio.h>
+#define YYBISON 1
+$/* %{ and %header{ and %union, during decl */
+#define YY_@_BISON 1
+#ifndef YY_@_COMPATIBILITY
+#ifndef YY_USE_CLASS
+#define YY_@_COMPATIBILITY 1
+#else
+#define YY_@_COMPATIBILITY 0
+#endif
+#endif
+
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifdef YYLTYPE
+#ifndef YY_@_LTYPE
+#define YY_@_LTYPE YYLTYPE
+#endif
+#endif
+#ifdef YYSTYPE
+#ifndef YY_@_STYPE
+#define YY_@_STYPE YYSTYPE
+#endif
+#endif
+#ifdef YYDEBUG
+#ifndef YY_@_DEBUG
+#define YY_@_DEBUG YYDEBUG
+#endif
+#endif
+#ifdef YY_@_STYPE
+#ifndef yystype
+#define yystype YY_@_STYPE
+#endif
+#endif
+/* use goto to be compatible */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 1
+#endif
+#endif
+
+/* use no goto to be clean in C++ */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 0
+#endif
+
+#ifndef YY_@_PURE
+$/* YY_@_PURE */
+#endif
+
+/* section apres lecture def, avant lecture grammaire S2 */
+$/* prefix */
+#ifndef YY_@_DEBUG
+$/* YY_@_DEBUG */
+#endif
+
+
+#ifndef YY_@_LSP_NEEDED
+$ /* YY_@_LSP_NEEDED*/
+#endif
+
+
+
+/* DEFAULT LTYPE*/
+#ifdef YY_@_LSP_NEEDED
+#ifndef YY_@_LTYPE
+typedef struct yyltype
+{
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+}
+yyltype;
+
+#define YY_@_LTYPE yyltype
+#endif
+#endif
+/* DEFAULT STYPE*/
+ /* We used to use `unsigned long' as YY_@_STYPE on MSDOS,
+ but it seems better to be consistent.
+ Most programs should declare their own type anyway. */
+
+#ifndef YY_@_STYPE
+#define YY_@_STYPE int
+#endif
+/* DEFAULT MISCELANEOUS */
+#ifndef YY_@_PARSE
+#define YY_@_PARSE yyparse
+#endif
+#ifndef YY_@_LEX
+#define YY_@_LEX yylex
+#endif
+#ifndef YY_@_LVAL
+#define YY_@_LVAL yylval
+#endif
+#ifndef YY_@_LLOC
+#define YY_@_LLOC yylloc
+#endif
+#ifndef YY_@_CHAR
+#define YY_@_CHAR yychar
+#endif
+#ifndef YY_@_NERRS
+#define YY_@_NERRS yynerrs
+#endif
+#ifndef YY_@_DEBUG_FLAG
+#define YY_@_DEBUG_FLAG yydebug
+#endif
+#ifndef YY_@_ERROR
+#define YY_@_ERROR yyerror
+#endif
+#ifndef YY_@_PARSE_PARAM
+#ifndef __STDC__
+#ifndef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_@_PARSE_PARAM
+#ifndef YY_@_PARSE_PARAM_DEF
+#define YY_@_PARSE_PARAM_DEF
+#endif
+#endif
+#endif
+#endif
+#ifndef YY_@_PARSE_PARAM
+#define YY_@_PARSE_PARAM void
+#endif
+#endif
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifdef YY_@_LTYPE
+#ifndef YYLTYPE
+#define YYLTYPE YY_@_LTYPE
+#else
+/* WARNING obsolete !!! user defined YYLTYPE not reported into generated header */
+#endif
+#endif
+#ifndef YYSTYPE
+#define YYSTYPE YY_@_STYPE
+#else
+/* WARNING obsolete !!! user defined YYSTYPE not reported into generated header */
+#endif
+#ifdef YY_@_PURE
+#ifndef YYPURE
+#define YYPURE YY_@_PURE
+#endif
+#endif
+#ifdef YY_@_DEBUG
+#ifndef YYDEBUG
+#define YYDEBUG YY_@_DEBUG
+#endif
+#endif
+#ifndef YY_@_ERROR_VERBOSE
+#ifdef YYERROR_VERBOSE
+#define YY_@_ERROR_VERBOSE YYERROR_VERBOSE
+#endif
+#endif
+#ifndef YY_@_LSP_NEEDED
+#ifdef YYLSP_NEEDED
+#define YY_@_LSP_NEEDED YYLSP_NEEDED
+#endif
+#endif
+#endif
+#ifndef YY_USE_CLASS
+/* TOKEN C */
+$ /* #defines tokens */
+#else
+/* CLASS */
+#ifndef YY_@_CLASS
+#define YY_@_CLASS @
+#endif
+#ifndef YY_@_INHERIT
+#define YY_@_INHERIT
+#endif
+#ifndef YY_@_MEMBERS
+#define YY_@_MEMBERS
+#endif
+#ifndef YY_@_LEX_BODY
+#define YY_@_LEX_BODY
+#endif
+#ifndef YY_@_ERROR_BODY
+#define YY_@_ERROR_BODY
+#endif
+#ifndef YY_@_CONSTRUCTOR_PARAM
+#define YY_@_CONSTRUCTOR_PARAM
+#endif
+#ifndef YY_@_CONSTRUCTOR_CODE
+#define YY_@_CONSTRUCTOR_CODE
+#endif
+#ifndef YY_@_CONSTRUCTOR_INIT
+#define YY_@_CONSTRUCTOR_INIT
+#endif
+/* choose between enum and const */
+#ifndef YY_@_USE_CONST_TOKEN
+#define YY_@_USE_CONST_TOKEN 0
+/* yes enum is more compatible with flex, */
+/* so by default we use it */
+#endif
+#if YY_@_USE_CONST_TOKEN != 0
+#ifndef YY_@_ENUM_TOKEN
+#define YY_@_ENUM_TOKEN yy_@_enum_token
+#endif
+#endif
+
+class YY_@_CLASS YY_@_INHERIT
+{
+public:
+#if YY_@_USE_CONST_TOKEN != 0
+/* static const int token ... */
+$ /* decl const */
+#else
+enum YY_@_ENUM_TOKEN { YY_@_NULL_TOKEN=0
+$ /* enum token */
+ }; /* end of enum declaration */
+#endif
+public:
+ int YY_@_PARSE (YY_@_PARSE_PARAM);
+#ifdef YY_@_PURE
+#else
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE YY_@_LLOC;
+#endif
+ int YY_@_NERRS;
+ int YY_@_CHAR;
+#endif
+#if YY_@_DEBUG != 0
+ int YY_@_DEBUG_FLAG; /* nonzero means print parse trace */
+#endif
+public:
+ YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM);
+public:
+ YY_@_MEMBERS
+};
+/* other declare folow */
+#if YY_@_USE_CONST_TOKEN != 0
+$ /* const YY_@_CLASS::token */
+#endif
+/*apres const */
+YY_@_CLASS::YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM) YY_@_CONSTRUCTOR_INIT
+{
+#if YY_@_DEBUG != 0
+YY_@_DEBUG_FLAG=0;
+#endif
+YY_@_CONSTRUCTOR_CODE;
+};
+#endif
+$ /* fattrs + tables */
+
+/* parser code folow */
+
+
+/* This is the parser code that is written into each bison parser
+ when the %semantic_parser declaration is not specified in the grammar.
+ It was written by Richard Stallman by simplifying the hairy parser
+ used when %semantic_parser is specified. */
+
+/* Note: dollar marks section change
+ the next is replaced by the list of actions, each action
+ as one case of the switch. */
+
+#if YY_@_USE_GOTO != 0
+/*
+ SUPRESSION OF GOTO : on some C++ compiler (sun c++)
+ the goto is strictly forbidden if any constructor/destructor
+ is used in the whole function (very stupid isn't it ?)
+ so goto are to be replaced with a 'while/switch/case construct'
+ here are the macro to keep some apparent compatibility
+*/
+#define YYGOTO(lb) {yy_gotostate=lb;continue;}
+#define YYBEGINGOTO enum yy_labels yy_gotostate=yygotostart; \
+ for(;;) switch(yy_gotostate) { case yygotostart: {
+#define YYLABEL(lb) } case lb: {
+#define YYENDGOTO } }
+#define YYBEGINDECLARELABEL enum yy_labels {yygotostart
+#define YYDECLARELABEL(lb) ,lb
+#define YYENDDECLARELABEL };
+#else
+/* macro to keep goto */
+#define YYGOTO(lb) goto lb
+#define YYBEGINGOTO
+#define YYLABEL(lb) lb:
+#define YYENDGOTO
+#define YYBEGINDECLARELABEL
+#define YYDECLARELABEL(lb)
+#define YYENDDECLARELABEL
+#endif
+/* LABEL DECLARATION */
+YYBEGINDECLARELABEL
+ YYDECLARELABEL(yynewstate)
+ YYDECLARELABEL(yybackup)
+/* YYDECLARELABEL(yyresume) */
+ YYDECLARELABEL(yydefault)
+ YYDECLARELABEL(yyreduce)
+ YYDECLARELABEL(yyerrlab) /* here on detecting error */
+ YYDECLARELABEL(yyerrlab1) /* here on error raised explicitly by an action */
+ YYDECLARELABEL(yyerrdefault) /* current state does not do anything special for the error token. */
+ YYDECLARELABEL(yyerrpop) /* pop the current state because it cannot handle the error token */
+ YYDECLARELABEL(yyerrhandle)
+YYENDDECLARELABEL
+/* ALLOCA SIMULATION */
+/* __HAVE_NO_ALLOCA */
+#ifdef __HAVE_NO_ALLOCA
+int __alloca_free_ptr(char *ptr,char *ref)
+{if(ptr!=ref) free(ptr);
+ return 0;}
+
+#define __ALLOCA_alloca(size) malloc(size)
+#define __ALLOCA_free(ptr,ref) __alloca_free_ptr((char *)ptr,(char *)ref)
+
+#ifdef YY_@_LSP_NEEDED
+#define __ALLOCA_return(num) \
+ return( __ALLOCA_free(yyss,yyssa)+\
+ __ALLOCA_free(yyvs,yyvsa)+\
+ __ALLOCA_free(yyls,yylsa)+\
+ (num))
+#else
+#define __ALLOCA_return(num) \
+ return( __ALLOCA_free(yyss,yyssa)+\
+ __ALLOCA_free(yyvs,yyvsa)+\
+ (num))
+#endif
+#else
+#define __ALLOCA_return(num) return(num)
+#define __ALLOCA_alloca(size) alloca(size)
+#define __ALLOCA_free(ptr,ref)
+#endif
+
+/* ENDALLOCA SIMULATION */
+
+#define yyerrok (yyerrstatus = 0)
+#define yyclearin (YY_@_CHAR = YYEMPTY)
+#define YYEMPTY -2
+#define YYEOF 0
+#define YYACCEPT __ALLOCA_return(0)
+#define YYABORT __ALLOCA_return(1)
+#define YYERROR YYGOTO(yyerrlab1)
+/* Like YYERROR except do call yyerror.
+ This remains here temporarily to ease the
+ transition to the new meaning of YYERROR, for GCC.
+ Once GCC version 2 has supplanted version 1, this can go. */
+#define YYFAIL YYGOTO(yyerrlab)
+#define YYRECOVERING() (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do\
+ if (YY_@_CHAR == YYEMPTY && yylen == 1)\
+ {\
+ YY_@_CHAR = (token), YY_@_LVAL = (value);\
+ yychar1 = YYTRANSLATE (YY_@_CHAR);\
+ YYPOPSTACK;\
+ YYGOTO(yybackup);\
+ }\
+ else\
+ {\
+ YY_@_ERROR ("syntax error: cannot back up"); YYERROR;\
+ }\
+while (0)
+
+#define YYTERROR 1
+#define YYERRCODE 256
+
+#ifndef YY_@_PURE
+/* UNPURE */
+#define YYLEX YY_@_LEX()
+#ifndef YY_USE_CLASS
+/* If nonreentrant, and not class , generate the variables here */
+int YY_@_CHAR; /* the lookahead symbol */
+ /* lookahead symbol */
+int YY_@_NERRS; /* number of parse errors so far */
+#ifdef YY_@_LSP_NEEDED
+YY_@_LTYPE YY_@_LLOC; /* location data for the lookahead */
+ /* symbol */
+#endif
+#endif
+
+
+#else
+/* PURE */
+#ifdef YY_@_LSP_NEEDED
+#define YYLEX YY_@_LEX(&YY_@_LVAL, &YY_@_LLOC)
+#else
+#define YYLEX YY_@_LEX(&YY_@_LVAL)
+#endif
+#endif
+#ifndef YY_USE_CLASS
+#if YY_@_DEBUG != 0
+int YY_@_DEBUG_FLAG; /* nonzero means print parse trace */
+/* Since this is uninitialized, it does not stop multiple parsers
+ from coexisting. */
+#endif
+#endif
+
+
+
+/* YYINITDEPTH indicates the initial size of the parser's stacks */
+
+#ifndef YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH is the maximum size the stacks can grow to
+ (effective only if the built-in stack extension method is used). */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+
+
+#if __GNUC__ > 1 /* GNU C and GNU C++ define this. */
+#define __yy_bcopy(FROM,TO,COUNT) __builtin_memcpy(TO,FROM,COUNT)
+#else /* not GNU C or C++ */
+
+/* This is the most reliable way to avoid incompatibilities
+ in available built-in functions on various systems. */
+
+#ifdef __cplusplus
+static void __yy_bcopy (char *from, char *to, int count)
+#else
+#ifdef __STDC__
+static void __yy_bcopy (char *from, char *to, int count)
+#else
+static void __yy_bcopy (from, to, count)
+ char *from;
+ char *to;
+ int count;
+#endif
+#endif
+{
+ register char *f = from;
+ register char *t = to;
+ register int i = count;
+
+ while (i-- > 0)
+ *t++ = *f++;
+}
+#endif
+
+int
+#ifdef YY_USE_CLASS
+ YY_@_CLASS::
+#endif
+ YY_@_PARSE(YY_@_PARSE_PARAM)
+#ifndef __STDC__
+#ifndef __cplusplus
+#ifndef YY_USE_CLASS
+/* parameter definition without protypes */
+YY_@_PARSE_PARAM_DEF
+#endif
+#endif
+#endif
+{
+ register int yystate;
+ register int yyn;
+ register short *yyssp;
+ register YY_@_STYPE *yyvsp;
+ int yyerrstatus; /* number of tokens to shift before error messages enabled */
+ int yychar1=0; /* lookahead token as an internal (translated) token number */
+
+ short yyssa[YYINITDEPTH]; /* the state stack */
+ YY_@_STYPE yyvsa[YYINITDEPTH]; /* the semantic value stack */
+
+ short *yyss = yyssa; /* refer to the stacks thru separate pointers */
+ YY_@_STYPE *yyvs = yyvsa; /* to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE yylsa[YYINITDEPTH]; /* the location stack */
+ YY_@_LTYPE *yyls = yylsa;
+ YY_@_LTYPE *yylsp;
+
+#define YYPOPSTACK (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK (yyvsp--, yyssp--)
+#endif
+
+ int yystacksize = YYINITDEPTH;
+
+#ifdef YY_@_PURE
+ int YY_@_CHAR;
+ YY_@_STYPE YY_@_LVAL;
+ int YY_@_NERRS;
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE YY_@_LLOC;
+#endif
+#endif
+
+ YY_@_STYPE yyval; /* the variable used to return */
+ /* semantic values from the action */
+ /* routines */
+
+ int yylen;
+/* start loop, in which YYGOTO may be used. */
+YYBEGINGOTO
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Starting parse\n");
+#endif
+ yystate = 0;
+ yyerrstatus = 0;
+ YY_@_NERRS = 0;
+ YY_@_CHAR = YYEMPTY; /* Cause a token to be read. */
+
+ /* Initialize stack pointers.
+ Waste one element of value and location stack
+ so that they stay on the same level as the state stack.
+ The wasted elements are never initialized. */
+
+ yyssp = yyss - 1;
+ yyvsp = yyvs;
+#ifdef YY_@_LSP_NEEDED
+ yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in yystate . */
+/* In all cases, when you get here, the value and location stacks
+ have just been pushed. so pushing a state here evens the stacks. */
+YYLABEL(yynewstate)
+
+ *++yyssp = yystate;
+
+ if (yyssp >= yyss + yystacksize - 1)
+ {
+ /* Give user a chance to reallocate the stack */
+ /* Use copies of these so that the &'s don't force the real ones into memory. */
+ YY_@_STYPE *yyvs1 = yyvs;
+ short *yyss1 = yyss;
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE *yyls1 = yyls;
+#endif
+
+ /* Get the current used size of the three stacks, in elements. */
+ int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+ /* Each stack pointer address is followed by the size of
+ the data in use in that stack, in bytes. */
+#ifdef YY_@_LSP_NEEDED
+ /* This used to be a conditional around just the two extra args,
+ but that might be undefined if yyoverflow is a macro. */
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yyls1, size * sizeof (*yylsp),
+ &yystacksize);
+#else
+ yyoverflow("parser stack overflow",
+ &yyss1, size * sizeof (*yyssp),
+ &yyvs1, size * sizeof (*yyvsp),
+ &yystacksize);
+#endif
+
+ yyss = yyss1; yyvs = yyvs1;
+#ifdef YY_@_LSP_NEEDED
+ yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+ /* Extend the stack our own way. */
+ if (yystacksize >= YYMAXDEPTH)
+ {
+ YY_@_ERROR("parser stack overflow");
+ __ALLOCA_return(2);
+ }
+ yystacksize *= 2;
+ if (yystacksize > YYMAXDEPTH)
+ yystacksize = YYMAXDEPTH;
+ yyss = (short *) __ALLOCA_alloca (yystacksize * sizeof (*yyssp));
+ __yy_bcopy ((char *)yyss1, (char *)yyss, size * sizeof (*yyssp));
+ __ALLOCA_free(yyss1,yyssa);
+ yyvs = (YY_@_STYPE *) __ALLOCA_alloca (yystacksize * sizeof (*yyvsp));
+ __yy_bcopy ((char *)yyvs1, (char *)yyvs, size * sizeof (*yyvsp));
+ __ALLOCA_free(yyvs1,yyvsa);
+#ifdef YY_@_LSP_NEEDED
+ yyls = (YY_@_LTYPE *) __ALLOCA_alloca (yystacksize * sizeof (*yylsp));
+ __yy_bcopy ((char *)yyls1, (char *)yyls, size * sizeof (*yylsp));
+ __ALLOCA_free(yyls1,yylsa);
+#endif
+#endif /* no yyoverflow */
+
+ yyssp = yyss + size - 1;
+ yyvsp = yyvs + size - 1;
+#ifdef YY_@_LSP_NEEDED
+ yylsp = yyls + size - 1;
+#endif
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+ if (yyssp >= yyss + yystacksize - 1)
+ YYABORT;
+ }
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+ YYGOTO(yybackup);
+YYLABEL(yybackup)
+
+/* Do appropriate processing given the current state. */
+/* Read a lookahead token if we need one and don't already have one. */
+/* YYLABEL(yyresume) */
+
+ /* First try to decide what to do without reference to lookahead token. */
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ YYGOTO(yydefault);
+
+ /* Not known => get a lookahead token if don't already have one. */
+
+ /* yychar is either YYEMPTY or YYEOF
+ or a valid token in external form. */
+
+ if (YY_@_CHAR == YYEMPTY)
+ {
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Reading a token: ");
+#endif
+ YY_@_CHAR = YYLEX;
+ }
+
+ /* Convert token to internal form (in yychar1) for indexing tables with */
+
+ if (YY_@_CHAR <= 0) /* This means end of input. */
+ {
+ yychar1 = 0;
+ YY_@_CHAR = YYEOF; /* Don't call YYLEX any more */
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Now at end of input.\n");
+#endif
+ }
+ else
+ {
+ yychar1 = YYTRANSLATE(YY_@_CHAR);
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ fprintf (stderr, "Next token is %d (%s", YY_@_CHAR, yytname[yychar1]);
+ /* Give the individual parser a way to print the precise meaning
+ of a token, for further debugging info. */
+#ifdef YYPRINT
+ YYPRINT (stderr, YY_@_CHAR, YY_@_LVAL);
+#endif
+ fprintf (stderr, ")\n");
+ }
+#endif
+ }
+
+ yyn += yychar1;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+ YYGOTO(yydefault);
+
+ yyn = yytable[yyn];
+
+ /* yyn is what to do for this token type in this state.
+ Negative => reduce, -yyn is rule number.
+ Positive => shift, yyn is new state.
+ New state is final state => don't bother to shift,
+ just return success.
+ 0, or most negative number => error. */
+
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ YYGOTO(yyerrlab);
+ yyn = -yyn;
+ YYGOTO(yyreduce);
+ }
+ else if (yyn == 0)
+ YYGOTO(yyerrlab);
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+ /* Shift the lookahead token. */
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Shifting token %d (%s), ", YY_@_CHAR, yytname[yychar1]);
+#endif
+
+ /* Discard the token being shifted unless it is eof. */
+ if (YY_@_CHAR != YYEOF)
+ YY_@_CHAR = YYEMPTY;
+
+ *++yyvsp = YY_@_LVAL;
+#ifdef YY_@_LSP_NEEDED
+ *++yylsp = YY_@_LLOC;
+#endif
+
+ /* count tokens shifted since error; after three, turn off error status. */
+ if (yyerrstatus) yyerrstatus--;
+
+ yystate = yyn;
+ YYGOTO(yynewstate);
+
+/* Do the default action for the current state. */
+YYLABEL(yydefault)
+
+ yyn = yydefact[yystate];
+ if (yyn == 0)
+ YYGOTO(yyerrlab);
+
+/* Do a reduction. yyn is the number of a rule to reduce with. */
+YYLABEL(yyreduce)
+ yylen = yyr2[yyn];
+ if (yylen > 0)
+ yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ int i;
+
+ fprintf (stderr, "Reducing via rule %d (line %d), ",
+ yyn, yyrline[yyn]);
+
+ /* Print the symbols being reduced, and their result. */
+ for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+ fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+ fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+ }
+#endif
+
+$ /* the action file gets copied in in place of this dollarsign */
+ yyvsp -= yylen;
+ yyssp -= yylen;
+#ifdef YY_@_LSP_NEEDED
+ yylsp -= yylen;
+#endif
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+ *++yyvsp = yyval;
+
+#ifdef YY_@_LSP_NEEDED
+ yylsp++;
+ if (yylen == 0)
+ {
+ *yylsp= YY_@_LLOC;
+ //yylsp->first_line = YY_@_LLOC.first_line;
+ //yylsp->first_column = YY_@_LLOC.first_column;
+ //yylsp->last_line = (yylsp-1)->last_line;
+ //yylsp->last_column = (yylsp-1)->last_column;
+ //yylsp->text = 0;
+ }
+ else
+ {
+ *yylsp = *(yylsp+yylen-1);
+ //yylsp->last_line = (yylsp+yylen-1)->last_line;
+ //yylsp->last_column = (yylsp+yylen-1)->last_column;
+ }
+#endif
+
+ /* Now "shift" the result of the reduction.
+ Determine what state that goes to,
+ based on the state we popped back to
+ and the rule number reduced by. */
+
+ yyn = yyr1[yyn];
+
+ yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+ if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+ yystate = yytable[yystate];
+ else
+ yystate = yydefgoto[yyn - YYNTBASE];
+
+ YYGOTO(yynewstate);
+
+YYLABEL(yyerrlab) /* here on detecting error */
+
+ if (! yyerrstatus)
+ /* If not already recovering from an error, report this error. */
+ {
+ ++YY_@_NERRS;
+
+#ifdef YY_@_ERROR_VERBOSE
+ yyn = yypact[yystate];
+
+ if (yyn > YYFLAG && yyn < YYLAST)
+ {
+ int size = 0;
+ char *msg;
+ int x, count;
+
+ count = 0;
+ /* Start X at -yyn if nec to avoid negative indexes in yycheck. */
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ size += strlen(yytname[x]) + 15, count++;
+ msg = (char *) malloc(size + 15);
+ if (msg != 0)
+ {
+ strcpy(msg, "parse error");
+
+ if (count < 5)
+ {
+ count = 0;
+ for (x = (yyn < 0 ? -yyn : 0);
+ x < (sizeof(yytname) / sizeof(char *)); x++)
+ if (yycheck[x + yyn] == x)
+ {
+ strcat(msg, count == 0 ? ", expecting `" : " or `");
+ strcat(msg, yytname[x]);
+ strcat(msg, "'");
+ count++;
+ }
+ }
+ YY_@_ERROR(msg);
+ free(msg);
+ }
+ else
+ YY_@_ERROR ("parse error; also virtual memory exceeded");
+ }
+ else
+#endif /* YY_@_ERROR_VERBOSE */
+ YY_@_ERROR("parse error");
+ }
+
+ YYGOTO(yyerrlab1);
+YYLABEL(yyerrlab1) /* here on error raised explicitly by an action */
+
+ if (yyerrstatus == 3)
+ {
+ /* if just tried and failed to reuse lookahead token after an error, discard it. */
+
+ /* return failure if at end of input */
+ if (YY_@_CHAR == YYEOF)
+ YYABORT;
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Discarding token %d (%s).\n", YY_@_CHAR, yytname[yychar1]);
+#endif
+
+ YY_@_CHAR = YYEMPTY;
+ }
+
+ /* Else will try to reuse lookahead token
+ after shifting the error token. */
+
+ yyerrstatus = 3; /* Each real token shifted decrements this */
+
+ YYGOTO(yyerrhandle);
+
+YYLABEL(yyerrdefault) /* current state does not do anything special for the error token. */
+
+#if 0
+ /* This is wrong; only states that explicitly want error tokens
+ should shift them. */
+ yyn = yydefact[yystate]; /* If its default is to accept any token, ok. Otherwise pop it.*/
+ if (yyn) YYGOTO(yydefault);
+#endif
+
+YYLABEL(yyerrpop) /* pop the current state because it cannot handle the error token */
+
+ if (yyssp == yyss) YYABORT;
+ yyvsp--;
+ yystate = *--yyssp;
+#ifdef YY_@_LSP_NEEDED
+ yylsp--;
+#endif
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ {
+ short *ssp1 = yyss - 1;
+ fprintf (stderr, "Error: state stack now");
+ while (ssp1 != yyssp)
+ fprintf (stderr, " %d", *++ssp1);
+ fprintf (stderr, "\n");
+ }
+#endif
+
+YYLABEL(yyerrhandle)
+
+ yyn = yypact[yystate];
+ if (yyn == YYFLAG)
+ YYGOTO(yyerrdefault);
+
+ yyn += YYTERROR;
+ if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+ YYGOTO(yyerrdefault);
+
+ yyn = yytable[yyn];
+ if (yyn < 0)
+ {
+ if (yyn == YYFLAG)
+ YYGOTO(yyerrpop);
+ yyn = -yyn;
+ YYGOTO(yyreduce);
+ }
+ else if (yyn == 0)
+ YYGOTO(yyerrpop);
+
+ if (yyn == YYFINAL)
+ YYACCEPT;
+
+#if YY_@_DEBUG != 0
+ if (YY_@_DEBUG_FLAG)
+ fprintf(stderr, "Shifting error token, ");
+#endif
+
+ *++yyvsp = YY_@_LVAL;
+#ifdef YY_@_LSP_NEEDED
+ *++yylsp = YY_@_LLOC;
+#endif
+
+ yystate = yyn;
+ YYGOTO(yynewstate);
+/* end loop, in which YYGOTO may be used. */
+ YYENDGOTO
+}
+
+/* END */
+$ /* section 3 */
+
+/* AFTER END , NEVER READ !!! */
+
diff --git a/tools/mhmake/src/bison.h b/tools/mhmake/src/bison.h
new file mode 100644
index 000000000..8bec41080
--- /dev/null
+++ b/tools/mhmake/src/bison.h
@@ -0,0 +1,252 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* before anything */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+#ifdef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_USE_CLASS
+#endif
+#else
+#endif
+#include <stdio.h>
+$ /* %{ and %header{ and %union, during decl */
+#ifndef YY_@_COMPATIBILITY
+#ifndef YY_USE_CLASS
+#define YY_@_COMPATIBILITY 1
+#else
+#define YY_@_COMPATIBILITY 0
+#endif
+#endif
+
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifdef YYLTYPE
+#ifndef YY_@_LTYPE
+#define YY_@_LTYPE YYLTYPE
+/* WARNING obsolete !!! user defined YYLTYPE not reported into generated header */
+/* use %define LTYPE */
+#endif
+#endif
+#ifdef YYSTYPE
+#ifndef YY_@_STYPE
+#define YY_@_STYPE YYSTYPE
+/* WARNING obsolete !!! user defined YYSTYPE not reported into generated header */
+/* use %define STYPE */
+#endif
+#endif
+#ifdef YYDEBUG
+#ifndef YY_@_DEBUG
+#define YY_@_DEBUG YYDEBUG
+/* WARNING obsolete !!! user defined YYDEBUG not reported into generated header */
+/* use %define DEBUG */
+#endif
+#endif
+#ifdef YY_@_STYPE
+#ifndef yystype
+#define yystype YY_@_STYPE
+#endif
+#endif
+/* use goto to be compatible */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 1
+#endif
+#endif
+
+/* use no goto to be clean in C++ */
+#ifndef YY_@_USE_GOTO
+#define YY_@_USE_GOTO 0
+#endif
+
+#ifndef YY_@_PURE
+$/* YY_@_PURE */
+#endif
+$/* prefix */
+#ifndef YY_@_DEBUG
+$/* YY_@_DEBUG */
+#endif
+#ifndef YY_@_LSP_NEEDED
+$ /* YY_@_LSP_NEEDED*/
+#endif
+/* DEFAULT LTYPE*/
+#ifdef YY_@_LSP_NEEDED
+#ifndef YY_@_LTYPE
+typedef
+ struct yyltype
+ {
+ int timestamp;
+ int first_line;
+ int first_column;
+ int last_line;
+ int last_column;
+ char *text;
+ }
+ yyltype;
+
+#define YY_@_LTYPE yyltype
+#endif
+#endif
+/* DEFAULT STYPE*/
+#ifndef YY_@_STYPE
+#define YY_@_STYPE int
+#endif
+/* DEFAULT MISCELANEOUS */
+#ifndef YY_@_PARSE
+#define YY_@_PARSE yyparse
+#endif
+#ifndef YY_@_LEX
+#define YY_@_LEX yylex
+#endif
+#ifndef YY_@_LVAL
+#define YY_@_LVAL yylval
+#endif
+#ifndef YY_@_LLOC
+#define YY_@_LLOC yylloc
+#endif
+#ifndef YY_@_CHAR
+#define YY_@_CHAR yychar
+#endif
+#ifndef YY_@_NERRS
+#define YY_@_NERRS yynerrs
+#endif
+#ifndef YY_@_DEBUG_FLAG
+#define YY_@_DEBUG_FLAG yydebug
+#endif
+#ifndef YY_@_ERROR
+#define YY_@_ERROR yyerror
+#endif
+
+#ifndef YY_@_PARSE_PARAM
+#ifndef __STDC__
+#ifndef __cplusplus
+#ifndef YY_USE_CLASS
+#define YY_@_PARSE_PARAM
+#ifndef YY_@_PARSE_PARAM_DEF
+#define YY_@_PARSE_PARAM_DEF
+#endif
+#endif
+#endif
+#endif
+#ifndef YY_@_PARSE_PARAM
+#define YY_@_PARSE_PARAM void
+#endif
+#endif
+
+/* TOKEN C */
+#ifndef YY_USE_CLASS
+
+#ifndef YY_@_PURE
+extern YY_@_STYPE YY_@_LVAL;
+#endif
+
+$ /* #defines token */
+/* after #define tokens, before const tokens S5*/
+#else
+#ifndef YY_@_CLASS
+#define YY_@_CLASS @
+#endif
+
+#ifndef YY_@_INHERIT
+#define YY_@_INHERIT
+#endif
+#ifndef YY_@_MEMBERS
+#define YY_@_MEMBERS
+#endif
+#ifndef YY_@_LEX_BODY
+#define YY_@_LEX_BODY
+#endif
+#ifndef YY_@_ERROR_BODY
+#define YY_@_ERROR_BODY
+#endif
+#ifndef YY_@_CONSTRUCTOR_PARAM
+#define YY_@_CONSTRUCTOR_PARAM
+#endif
+/* choose between enum and const */
+#ifndef YY_@_USE_CONST_TOKEN
+#define YY_@_USE_CONST_TOKEN 0
+/* yes enum is more compatible with flex, */
+/* so by default we use it */
+#endif
+#if YY_@_USE_CONST_TOKEN != 0
+#ifndef YY_@_ENUM_TOKEN
+#define YY_@_ENUM_TOKEN yy_@_enum_token
+#endif
+#endif
+
+class YY_@_CLASS YY_@_INHERIT
+{
+public:
+#if YY_@_USE_CONST_TOKEN != 0
+/* static const int token ... */
+$ /* decl const */
+#else
+enum YY_@_ENUM_TOKEN { YY_@_NULL_TOKEN=0
+$ /* enum token */
+ }; /* end of enum declaration */
+#endif
+public:
+ int YY_@_PARSE(YY_@_PARSE_PARAM);
+#ifdef YY_@_PURE
+#else
+#ifdef YY_@_LSP_NEEDED
+ YY_@_LTYPE YY_@_LLOC;
+#endif
+ int YY_@_NERRS;
+ int YY_@_CHAR;
+#endif
+#if YY_@_DEBUG != 0
+public:
+ int YY_@_DEBUG_FLAG; /* nonzero means print parse trace */
+#endif
+public:
+ YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM);
+public:
+ YY_@_MEMBERS
+};
+/* other declare folow */
+#endif
+
+
+#if YY_@_COMPATIBILITY != 0
+/* backward compatibility */
+#ifndef YYSTYPE
+#define YYSTYPE YY_@_STYPE
+#endif
+
+#ifndef YYLTYPE
+#define YYLTYPE YY_@_LTYPE
+#endif
+#ifndef YYDEBUG
+#ifdef YY_@_DEBUG
+#define YYDEBUG YY_@_DEBUG
+#endif
+#endif
+
+#endif
+/* END */
+$ /* section 3 %header{ */
+ /* AFTER END , NEVER READ !!! */
+
+
+
diff --git a/tools/mhmake/src/build.cpp b/tools/mhmake/src/build.cpp
new file mode 100644
index 000000000..865ac8e2c
--- /dev/null
+++ b/tools/mhmake/src/build.cpp
@@ -0,0 +1,1703 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "mhmakefileparser.h"
+#include "mhmakeparser.h"
+#include "rule.h"
+#include "util.h"
+
+/* Calling py2exe is only implemented on windows for now. */
+#ifdef WIN32
+static const string &GetPythonExe();
+
+/* Python exe create script in parts:
+import zipfile,tempfile,shutil,os
+
+def UpdateZipFile(SrcZip,DestZip):
+ NewZip=tempfile.mktemp('.zip')
+
+ Src=zipfile.ZipFile(SrcZip)
+
+ New=zipfile.ZipFile(NewZip,'w')
+
+ InFile={}
+ for name in Src.namelist():
+ InFile[name]=1
+ New.writestr(name,Src.read(name))
+ Src.close()
+
+ try:
+ Dest=zipfile.ZipFile(DestZip)
+
+ for name in Dest.namelist():
+ if not InFile.has_key(name):
+ New.writestr(name,Dest.read(name))
+ Dest.close()
+ except IOError:
+ pass
+ New.close()
+
+ shutil.move(NewZip,DestZip)
+
+OutFileName=tempfile.mktemp('.py')
+pOut=open(OutFileName,'w')
+
+Script=r'<PYTHONSCRIPT>'
+DirScript=os.path.split(Script)[0]
+pOut.write(r'''
+
+from distutils.core import setup
+import py2exe
+import sys
+sys.path.append(r'%s')
+setup(zipfile=None, console=[r'%s'])
+'''%(DirScript,Script))
+
+pOut.close()
+
+import os
+
+stdin,stdout=os.popen4(r'"<PYTHONEXE>" %s py2exe'%OutFileName);
+stdout.read()
+stdout.close()
+stdin.close()
+
+OutDir=os.path.split(Script)[0]
+
+import shutil
+def CopyFiles(Src,Dest):
+ for File in os.listdir(Src):
+ SrcDir=os.path.join(Src,File)
+ if File=='library.zip':
+ UpdateZipFile(os.path.join(Src,File),os.path.join(Dest,File))
+ elif os.path.isdir(SrcDir):
+ DestDir=os.path.join(Dest,File)
+ os.mkdir(DestDir)
+ CopyFiles(SrcDir,DestDir)
+ else:
+ shutil.copy(os.path.join(Src,File),os.path.join(Dest,File))
+
+if os.path.isdir('dist'):
+ CopyFiles('dist',OutDir)
+
+ try:
+ shutil.rmtree('dist')
+ except:
+ pass
+ try:
+ shutil.rmtree('build')
+ except:
+ pass
+os.remove(OutFileName)
+*/
+
+static const string PythonScriptPart1=
+"import zipfile,tempfile,shutil,os\n"
+"\n"
+"def UpdateZipFile(SrcZip,DestZip):\n"
+" NewZip=tempfile.mktemp('.zip')\n"
+"\n"
+" Src=zipfile.ZipFile(SrcZip)\n"
+"\n"
+" New=zipfile.ZipFile(NewZip,'w')\n"
+"\n"
+" InFile={}\n"
+" for name in Src.namelist():\n"
+" InFile[name]=1\n"
+" New.writestr(name,Src.read(name))\n"
+" Src.close()\n"
+"\n"
+" try:\n"
+" Dest=zipfile.ZipFile(DestZip)\n"
+"\n"
+" for name in Dest.namelist():\n"
+" if not InFile.has_key(name):\n"
+" New.writestr(name,Dest.read(name))\n"
+" Dest.close()\n"
+" except IOError:\n"
+" pass\n"
+" New.close()\n"
+"\n"
+" shutil.move(NewZip,DestZip)\n"
+"\n"
+"OutFileName=tempfile.mktemp('.py')\n"
+"pOut=open(OutFileName,'w')\n"
+"\n"
+"Script=r'"
+;
+
+static const string PythonScriptPart2=
+"'\n"
+"DirScript=os.path.split(Script)[0]\n"
+"\n"
+"pOut.write(r'''\n"
+"\n"
+"from distutils.core import setup\n"
+"import py2exe\n"
+"import sys\n"
+"sys.path.append(r'%s')\n"
+"setup(console=[r'%s'])\n"
+"'''%(DirScript,Script))\n"
+"\n"
+"pOut.close()\n"
+"\n"
+"import os\n"
+"\n"
+"stdin,stdout=os.popen4(r'"
+;
+
+static const string PythonScriptPart3=
+"%s py2exe'%OutFileName);\n"
+"stdout.read()\n"
+"stdout.close()\n"
+"stdin.close()\n"
+"\n"
+"OutDir=os.path.split(Script)[0]\n"
+"\n"
+"import shutil\n"
+"def CopyFiles(Src,Dest):\n"
+" for File in os.listdir(Src):\n"
+" SrcDir=os.path.join(Src,File)\n"
+" if File=='library.zip':\n"
+" UpdateZipFile(os.path.join(Src,File),os.path.join(Dest,File))\n"
+" elif os.path.isdir(SrcDir):\n"
+" DestDir=os.path.join(Dest,File)\n"
+" os.mkdir(DestDir)\n"
+" CopyFiles(SrcDir,DestDir)\n"
+" else:\n"
+" shutil.copy(os.path.join(Src,File),os.path.join(Dest,File))\n"
+"\n"
+"if os.path.isdir('dist'):\n"
+" CopyFiles('dist',OutDir)\n"
+"\n"
+" try:\n"
+" shutil.rmtree('dist')\n"
+" except:\n"
+" pass\n"
+" try:\n"
+" shutil.rmtree('build')\n"
+" except:\n"
+" pass\n"
+"os.remove(OutFileName)\n"
+;
+/*****************************************************************************/
+/* Converts a python script to an executable if py2exe is installed */
+
+void mhmakefileparser::CreatePythonExe(const string &FullCommand)
+{
+ /* First create a python script to run */
+ cout << "Converting "<<FullCommand<<endl;
+
+ string PythonScript;
+ PythonScript+=PythonScriptPart1;
+ PythonScript+=FullCommand;
+ PythonScript+=PythonScriptPart2;
+ PythonScript+=GetPythonExe();
+ PythonScript+=PythonScriptPart3;
+
+ char Filename[10];
+ int Nr=0;
+ FILE *pFile=(FILE*)1;
+ while (1)
+ {
+ sprintf(Filename,"tmp%d.py",Nr);
+ pFile=fopen(Filename,"r");
+ if (!pFile)
+ break;
+ fclose(pFile);
+ Nr++;
+ }
+ pFile=fopen(Filename,"w");
+ fprintf(pFile,"%s",PythonScript.c_str());
+ fclose(pFile);
+
+ string GenExeCommand=GetPythonExe();
+ GenExeCommand+=Filename;
+
+ string Output;
+ ExecuteCommand(GenExeCommand,&Output);
+
+ remove(Filename);
+}
+#endif
+
+/*****************************************************************************/
+#ifndef WIN32
+static int SearchPath(void *NotUsed, const char *szCommand, const char *pExt, int Len, char *szFullCommand,char **pFilePart)
+{
+ string Command(szCommand);
+ if (pExt)
+ Command+=pExt;
+ vector< refptr<fileinfo> >::iterator It;
+ vector< refptr<fileinfo> >::iterator ItEnd;
+
+ refptr<fileinfo> CommandFile=GetFileInfo(Command);
+ if (CommandFile->Exists())
+ {
+ goto found;
+ }
+ static vector< refptr<fileinfo> > vSearchPath;
+ if (!vSearchPath.size())
+ {
+ char Path[1024];
+ char *pPath=getenv(PATH);
+ if (!pPath)
+ return 0;
+ strcpy(Path,pPath); // To be able to use strtok
+ char *pTok=strtok(Path,OSPATHENVSEPSTR);
+ while (pTok)
+ {
+ vSearchPath.push_back(GetFileInfo(pTok));
+ pTok=strtok(NULL,OSPATHENVSEPSTR);
+ }
+ }
+ It=vSearchPath.begin();
+ ItEnd=vSearchPath.end();
+ while (It!=ItEnd)
+ {
+ CommandFile=GetFileInfo(Command,*It);
+ if (CommandFile->Exists())
+ goto found;
+ It++;
+ }
+
+ return 0;
+
+found:
+ string FullCommand=CommandFile->GetFullFileName();
+ int CommandLen=FullCommand.size();
+ if (CommandLen>Len-1)
+ {
+ cerr << "Fatal error: Command to long: "<<FullCommand<<endl;
+ throw(1);
+ }
+ strcpy(szFullCommand,FullCommand.c_str());
+ return 1;
+}
+#endif
+
+/*****************************************************************************/
+static string SearchCommand(const string &Command, const string &Extension="")
+{
+ char FullCommand[MAX_PATH]="";
+ unsigned long Size=sizeof(FullCommand);
+ const char *pExt;
+ if (Extension.empty())
+ pExt=NULL;
+ else
+ pExt=Extension.c_str();
+ if (SearchPath(NULL,Command.c_str(),pExt,MAX_PATH,FullCommand,NULL))
+ return FullCommand;
+#ifdef WIN32
+ /* See if we have a path for python.exe in the registry */
+ HKEY hKey;
+ string RegEntry=Command;
+ if (pExt)
+ {
+ RegEntry+=Extension;
+ }
+ string KeyName=string("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\")+RegEntry;
+ if (ERROR_SUCCESS!=RegOpenKey(HKEY_LOCAL_MACHINE,KeyName.c_str(),&hKey))
+ return FullCommand;
+
+ RegQueryValueEx(hKey,NULL,NULL,NULL,(LPBYTE)FullCommand,&Size);
+
+ RegCloseKey(hKey);
+#endif
+
+ return FullCommand;
+}
+
+/*****************************************************************************/
+/* Deletes a complete directory or files with wildcard. It can be assumed that the Directory passed does exist */
+static bool DeleteDir(const string &Dir,const string WildSearch="*",bool bRemoveDir=true)
+{
+ bool Error=false;
+ string Pattern=Dir+OSPATHSEP+WildSearch;
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ HANDLE hFind=FindFirstFile(Pattern.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ return Error;
+ }
+
+ do
+ {
+ /* Only handle items which are not . and .. */
+ if (FindData.cFileName[0]!='.' || (FindData.cFileName[1] && (FindData.cFileName[1]!='.' || FindData.cFileName[2])) )
+ {
+ string FileName=Dir+OSPATHSEP+FindData.cFileName;
+ if (FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ {
+ Error = DeleteDir(FileName);
+ }
+ else
+ {
+ Error = (-1==remove(FileName.c_str()));
+ }
+ }
+ } while (FindNextFile(hFind,&FindData));
+
+ FindClose(hFind);
+
+ if (bRemoveDir)
+ Error = (0==RemoveDirectory(Dir.c_str()));
+#else
+ glob_t Res;
+ if (glob (Pattern.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
+ return Error;
+
+ for (int i=0; i<Res.gl_pathc; i++)
+ {
+ int Len=strlen(Res.gl_pathv[i])-1;
+ if (Res.gl_pathv[i][Len]=='/')
+ {
+ Res.gl_pathv[i][Len]=0;
+ Error = DeleteDir(Res.gl_pathv[i]);
+ }
+ else
+ {
+ Error = (-1==remove(Res.gl_pathv[i]));
+ }
+ }
+
+ globfree(&Res);
+
+ if (bRemoveDir)
+ Error = (-1==remove(Dir.c_str()));
+#endif
+
+ return Error;
+}
+
+/*****************************************************************************/
+static bool DeleteFiles(const string &Params)
+{
+ bool IgnoreError=false;
+ vector< refptr<fileinfo> > Files;
+
+ // First check if the first parameter is -e meaning don't give an error when file does not exist
+ if (Params[1]=='-')
+ {
+ if (Params[2]=='e')
+ {
+ IgnoreError=true;
+ SplitToItems(Params.substr(4),Files);
+ }
+ else
+ {
+ cerr << "Invalid option "<<Params[1]<<" in del statement\n";
+ return false;
+ }
+ }
+ else
+ {
+ SplitToItems(Params,Files);
+ }
+
+ vector< refptr<fileinfo> >::const_iterator It=Files.begin();
+ while (It!=Files.end())
+ {
+ refptr<fileinfo> pFile=*It++;
+ string DirSearch="*";
+ bool bRemoveDir=true;
+
+ /* Now check if there is a wildcard */
+ if (pFile->GetFullFileName().find('*')!=string::npos)
+ {
+ DirSearch=pFile->GetName();
+ pFile=pFile->GetDir();
+ bRemoveDir=false;
+ }
+
+ pFile->InvalidateDate();
+ if (IgnoreError && !pFile->Exists() && !pFile->IsDir())
+ {
+ continue;
+ }
+
+ const string &FileName=pFile->GetFullFileName();
+ if (pFile->IsDir())
+ {
+ if (DeleteDir(FileName,DirSearch,bRemoveDir) && !IgnoreError)
+ {
+ cerr << "Error deleting "<<FileName<<endl;
+ return false;
+ }
+ }
+ else
+ {
+ if (-1==remove(FileName.c_str()) && !IgnoreError)
+ {
+ cerr << "Error deleting "<<FileName<<endl;
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+/*****************************************************************************/
+/* pDest can be a directory or a file */
+static bool CopyFile(refptr<fileinfo> pSrc, refptr<fileinfo> pDest)
+{
+ if (pDest->IsDir())
+ {
+ pDest=GetFileInfo(pSrc->GetName(),pDest);
+ }
+ string SrcFileName=pSrc->GetFullFileName();
+ string DestFileName=pDest->GetFullFileName();
+
+ /* Now copy the file */
+ FILE *pSrcFile=fopen(SrcFileName.c_str(),"rb");
+ if (!pSrcFile)
+ {
+ cerr << "copy: error opening file "<<SrcFileName<<endl;
+ return false;
+ }
+ FILE *pDestFile=fopen(DestFileName.c_str(),"wb");
+ if (!pDestFile)
+ {
+ cerr << "copy: error creating file "<<DestFileName<<endl;
+ return false;
+ }
+ char Buf[4096];
+ int Ret;
+
+ while ( (Ret=fread(Buf,1,sizeof(Buf),pSrcFile)) > 0)
+ {
+ fwrite(Buf,1,Ret,pDestFile);
+ }
+ fclose(pSrcFile);
+ fclose(pDestFile);
+ pDest->InvalidateDate();
+ return true;
+}
+
+/*****************************************************************************/
+/* Copies a complete directory to a destination (currenlty not recursive */
+static bool CopyDir(refptr<fileinfo> pDir,refptr<fileinfo> pDest,const string WildSearch="*")
+{
+ bool Error=true;
+ string Dir=pDir->GetFullFileName()+OSPATHSEP;
+ string Pattern=Dir+WildSearch;
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ HANDLE hFind=FindFirstFile(Pattern.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ return Error;
+ }
+
+ do
+ {
+ /* Only handle items which are not . and .. */
+ if (FindData.cFileName[0]!='.' || (FindData.cFileName[1] && (FindData.cFileName[1]!='.' || FindData.cFileName[2])) )
+ {
+ string FileName=Dir+FindData.cFileName;
+ refptr<fileinfo> pSrc=GetFileInfo(FileName);
+
+ if (pSrc->IsDir())
+ {
+ /* Currently we do not do a recursion */
+ }
+ else
+ {
+ Error = CopyFile(pSrc,pDest);
+ }
+ }
+ } while (FindNextFile(hFind,&FindData));
+
+ FindClose(hFind);
+#else
+ glob_t Res;
+ if (glob (Pattern.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
+ return Error;
+
+ for (int i=0; i<Res.gl_pathc; i++)
+ {
+ int Len=strlen(Res.gl_pathv[i])-1;
+ if (Res.gl_pathv[i][Len]=='/')
+ {
+ Res.gl_pathv[i][Len]=0;
+ /* Do not recurs for the moment: Error = CopyDir(Res.gl_pathv[i]); */
+ }
+ else
+ {
+ Error = CopyFile(GetFileInfo(Res.gl_pathv[i]),pDest);
+ }
+ }
+
+ globfree(&Res);
+#endif
+
+ return Error;
+}
+
+/*****************************************************************************/
+static bool EchoCommand(const string &Params)
+{
+ // Find the first > character
+ int Pos=Params.find_first_of('>');
+ if (Pos==string::npos)
+ {
+ // Just echo it
+ cout << Params << endl;
+ }
+ else
+ {
+ FILE *pfFile;
+ /* Extra the filename */
+ string Filename;
+ if (Params[Pos+1]=='>')
+ {
+ NextItem(Params.substr(Pos+2).c_str(),Filename);
+ refptr<fileinfo> pFile=GetFileInfo(Filename);
+ // Open file in append
+ pfFile=fopen(pFile->GetFullFileName().c_str(),"a");
+ }
+ else
+ {
+ NextItem(Params.substr(Pos+1).c_str(),Filename);
+ refptr<fileinfo> pFile=GetFileInfo(Filename);
+ pfFile=fopen(pFile->GetFullFileName().c_str(),"w");
+ }
+ if (!pfFile)
+ {
+ cerr << "Error opening file "<<Filename<<endl;
+ return false;
+ }
+ int Begin=0;
+ while (Params[Begin]==' ' || Params[Begin] == '\t') Begin++; // Strip leading white space
+ string EchoStr=Params.substr(Begin,Pos-1)+"\n";
+ if (EchoStr.length()!=fwrite(EchoStr.c_str(),1,EchoStr.length(),pfFile))
+ {
+ cerr << "Error writing file "<<Filename<<endl;
+ return false;
+ }
+ fclose(pfFile);
+ }
+ return true;
+}
+
+/*****************************************************************************/
+static bool CopyFiles(const string &Params)
+{
+ vector< refptr<fileinfo> > Files;
+
+ SplitToItems(Params,Files);
+
+ int NrSrcs=Files.size()-1;
+
+ if (NrSrcs<1)
+ {
+ cerr << "Wrong number of arguments in copy: "<<Params<<endl;
+ return false;
+ }
+
+ refptr<fileinfo> pDest=Files[NrSrcs];
+ if (NrSrcs>1 && !pDest->IsDir())
+ {
+ cerr << "copy: Destination must be a directory when more then one source : "<<Params<<endl;
+ return false;
+ }
+
+ for (int i=0; i<NrSrcs; i++)
+ {
+ refptr<fileinfo> pSrc=Files[i];
+
+ string SrcFileName=pSrc->GetFullFileName();
+
+ if (pSrc->IsDir())
+ {
+ SrcFileName+=OSPATHSEPSTR"*";
+ pSrc=GetFileInfo(SrcFileName);
+ }
+
+ //cerr << "copy "<<pSrc->GetFullFileName()<<" "<<pDest->GetFullFileName()<<endl;
+ /* Now check if there is a wildcard */
+ if (SrcFileName.find('*')!=string::npos)
+ {
+ CopyDir(pSrc->GetDir(), pDest, pSrc->GetName());
+ }
+ else
+ {
+ if (!CopyFile(pSrc,pDest))
+ {
+ cerr << "copy: Error copying file: " << Params << endl;
+ return false;
+ }
+ }
+ }
+
+ return true;
+
+}
+
+/*****************************************************************************/
+static bool TouchFiles(const string &Params)
+{
+ vector< refptr<fileinfo> > Files;
+
+ SplitToItems(Params,Files);
+
+ vector< refptr<fileinfo> >::const_iterator It=Files.begin();
+ while (It!=Files.end())
+ {
+ refptr<fileinfo> pFile=*It++;
+ const string &FileName=pFile->GetFullFileName();
+
+ /* Since this can be part of a list of commands for a certain rule, and it is possible that the file
+ * was generated by one on the previous commands, we first need the invalidate the date so that the
+ * existance checking is done again */
+ pFile->InvalidateDate();
+
+ if (pFile->IsDir())
+ {
+ cerr << "touch: Cannot touch a directory: " << FileName << endl;
+ return false;
+ }
+ if (pFile->Exists())
+ {
+ int fd;
+ char c;
+ int status = 0;
+ struct stat st;
+ int Ret;
+ int saved_errno = 0;
+
+ fd = open (FileName.c_str(), O_RDWR);
+ if (fd<0)
+ {
+ st.st_size=0;
+ }
+ else
+ {
+ if (fstat (fd, &st) < 0)
+ {
+ cerr << "touch: Cannot stat file " << FileName << endl;
+ return false;
+ }
+ }
+
+ if (st.st_size == 0)
+ {
+ FILE *pFile;
+ if (fd>=0 && close(fd) < 0)
+ {
+ cerr << "touch: Error closing file " << FileName << endl;
+ return false;
+ }
+ /*Re-Create an empty file */
+ pFile=fopen(FileName.c_str(),"wb");
+ if (!pFile)
+ {
+ cerr << "touch: Cannot create file: " << FileName << endl;
+ return false;
+ }
+ fclose(pFile);
+ }
+ else
+ {
+ Ret=read (fd, &c, sizeof(c));
+ if (Ret!=sizeof(c) && Ret!=EOF)
+ {
+ cerr << "touch: Cannot read file " << FileName << ": "<<Ret<<endl;
+ return false;
+ }
+ if (lseek (fd, (off_t) 0, SEEK_SET) < 0)
+ {
+ cerr << "touch: Error changing file pointer " << FileName << endl;
+ return false;
+ }
+ if (write (fd, &c, sizeof c) != sizeof(c))
+ {
+ cerr << "touch: Error writing file " << FileName << endl;
+ return false;
+ }
+ if (close (fd) < 0)
+ {
+ cerr << "touch: Error closing file " << FileName << endl;
+ return false;
+ }
+ }
+ }
+ else
+ {
+ /* Create an empty file */
+ FILE *pFile=fopen(FileName.c_str(),"wb");
+ if (!pFile)
+ {
+ cerr << "touch: Cannot create file: " << FileName << endl;
+ return false;
+ }
+ fclose(pFile);
+ }
+ pFile->InvalidateDate();
+ }
+ return true;
+}
+
+/*****************************************************************************/
+static const string &GetPythonExe()
+{
+ static string PythonExe;
+ if (PythonExe.empty())
+ {
+ string FullCommand=SearchCommand(PYTHONEXE);
+ if (!FullCommand.empty())
+ {
+ PythonExe="\""+FullCommand+"\" ";
+ }
+ else
+ {
+ cerr<<"python executable not found in path and registry.\n";
+ exit(1);
+ }
+ }
+ return PythonExe;
+}
+
+/*****************************************************************************/
+static const string &GetComspec()
+{
+ static string Comspec;
+ if (Comspec.empty())
+ {
+ const char *pComspec=getenv(COMSPEC);
+ if (pComspec)
+ {
+ Comspec=getenv(COMSPEC);
+ #ifdef WIN32
+ Comspec+=" /c ";
+ #else
+ Comspec+=" -c \"";
+ #endif
+ }
+ else
+ {
+ #ifdef WIN32
+ Comspec="cmd.exe /c ";
+ #else
+ Comspec="sh -c \"";
+ #endif
+ }
+ }
+ return Comspec;
+}
+
+/*****************************************************************************/
+string mhmakefileparser::GetFullCommand(string Command)
+{
+ map<string,string>::iterator pFound=m_CommandCache.find(Command);
+ string OriCommand=Command;
+ if (pFound==m_CommandCache.end())
+ {
+ bool Found=false;
+ // Not found in the stack, search in the environment path
+ // Check if an extension is specified
+ const char *pBeg=Command.c_str();
+ const char *pEnd=pBeg+Command.length()-1;
+ bool HasExt=false;
+ while (pEnd>pBeg && *pEnd!=OSPATHSEP)
+ {
+ if (*pEnd=='.')
+ {
+ HasExt=true;
+ break;
+ }
+ pEnd--;
+ }
+ if (HasExt)
+ {
+ string FullCommand=SearchCommand(Command);
+ if (!FullCommand.empty())
+ {
+ Found=true;
+ Command=FullCommand;
+ }
+ }
+ else
+ {
+ static bool s_Py2ExeInstalled=true;
+ /* First check for special internal commands */
+ if (OriCommand=="del")
+ {
+ m_CommandCache[OriCommand]="del";
+ return Command;
+ }
+ // Try with different extensions
+ string FullCommand=SearchCommand(Command,EXEEXT);
+ if (!FullCommand.empty())
+ {
+ Found=true;
+ #ifdef WIN32
+ /* Check if a python script also exists, is so try generating the executable again. */
+ string PythonFullCommand=SearchCommand(Command,".py");
+ Command=FullCommand;
+ if (!PythonFullCommand.empty()&&s_Py2ExeInstalled)
+ {
+ refptr<fileinfo> pExeFile=GetFileInfo(FullCommand);
+ refptr<fileinfo> pPyFile=GetFileInfo(PythonFullCommand);
+ bool bBuild=false;
+ if (pExeFile->GetDate().IsOlder(pPyFile->GetDate()))
+ {
+ bBuild=true;
+ }
+ if (!bBuild)
+ {
+ set< refptr<fileinfo> > Autodeps;
+ GetAutoDeps(pPyFile, Autodeps);
+ set< refptr<fileinfo> >::iterator It=Autodeps.begin();
+ while (It!=Autodeps.end())
+ {
+ if (pExeFile->GetDate().IsOlder((*It)->GetDate()))
+ {
+ bBuild=true;
+ break;
+ }
+ It++;
+ }
+ }
+ if (bBuild)
+ {
+ if (pExeFile->Exists())
+ remove(pExeFile->GetFullFileName().c_str());
+ CreatePythonExe(PythonFullCommand);
+ // Invalidate the exe date since it could have been recreated by the CreatePythonExe
+ pExeFile->InvalidateDate();
+ }
+
+ }
+ #else
+ Command=FullCommand;
+ #endif
+ }
+ else
+ {
+ FullCommand=SearchCommand(Command,".py");
+ if (!FullCommand.empty())
+ {
+ Found=true;
+ #ifdef WIN32
+ /* Now first try to create an executable for it */
+ if (s_Py2ExeInstalled)
+ {
+ refptr<fileinfo> pExeFile;
+ CreatePythonExe(FullCommand);
+ string ExeFullCommand=SearchCommand(Command,EXEEXT);
+ if (!ExeFullCommand.empty())
+ {
+ pExeFile=GetFileInfo(ExeFullCommand);
+ pExeFile->InvalidateDate(); // The file was just generated, make sure the correct date is taken.
+ }
+ if (ExeFullCommand.empty() || !pExeFile->Exists())
+ {
+ s_Py2ExeInstalled=false;
+ cout << "\nWarning: cannot convert "<<FullCommand<<".\nCompilation will be faster by installing py2exe.\n\n";
+ Command=GetPythonExe()+"\""+FullCommand+"\"";
+ }
+ else
+ Command=ExeFullCommand;
+ }
+ else
+ #endif
+ Command=GetPythonExe()+FullCommand;
+ }
+ }
+ }
+ if (!Found)
+ {
+ if (Command.find(" ")!=string::npos)
+ {
+ #ifdef WIN32
+ Command=GetComspec()+"\""+Command+"\"";
+ #else
+ // In linux excape the spaces in commands
+ const char *pTmp=Command.c_str();
+ string NewCommand="";
+ string Space="";
+ string SpaceString="\\ ";
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ NewCommand+=Space;
+ NewCommand+=Item;
+ Space=SpaceString;
+ }
+ Command=GetComspec()+NewCommand;
+ #endif
+ }
+ else
+ Command=GetComspec()+Command;
+ }
+ m_CommandCache[OriCommand]=Command;
+ return Command;
+ }
+ return pFound->second;
+}
+
+static bool OsExeCommand(const string &Command,const string &Params,bool IgnoreError,string *pOutput)
+{
+#ifdef WIN32
+ STARTUPINFO StartupInfo;
+ memset(&StartupInfo,0,sizeof(StartupInfo));
+ StartupInfo.cb=sizeof(STARTUPINFO);
+ PROCESS_INFORMATION ProcessInfo;
+ string FullCommandLine;
+ string ComSpec=GetComspec();
+
+ if (Command.substr(0,ComSpec.size())==ComSpec)
+ {
+ string tmpCommand=Command.substr(ComSpec.size(),Command.size());
+ FullCommandLine=ComSpec;
+ if (tmpCommand.find(' ')!=string::npos)
+ FullCommandLine+="\""+tmpCommand+"\""+Params;
+ else
+ FullCommandLine+=tmpCommand+Params;
+ }
+ else
+ {
+ const string PythonExe=GetPythonExe();
+ if (Command.find(' ')!=string::npos &&
+ !(Command.substr(0,PythonExe.size())==PythonExe))
+ FullCommandLine="\""+Command+"\""+Params;
+ else
+ FullCommandLine=Command+Params;
+ }
+ char *pFullCommand=new char[FullCommandLine.length()+1];
+ strcpy(pFullCommand,FullCommandLine.c_str());
+
+ if (pOutput || g_Quiet)
+ {
+ HANDLE hChildStdinRd;
+ HANDLE hChildStdinWr;
+ HANDLE hChildStdoutRd;
+ HANDLE hChildStdoutWr;
+ HANDLE hChildStdinWrDup;
+ HANDLE hChildStdoutRdDup;
+ SECURITY_ATTRIBUTES saAttr;
+ BOOL fSuccess;
+
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (!CreatePipe(&hChildStdinRd, &hChildStdinWr, &saAttr, 0))
+ return false;
+
+ /* Create new output read handle and the input write handle. Set
+ * the inheritance properties to FALSE. Otherwise, the child inherits
+ * the these handles; resulting in non-closeable handles to the pipes
+ * being created. */
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdinWr,
+ GetCurrentProcess(), &hChildStdinWrDup, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!fSuccess) return false;
+ /* Close the inheritable version of ChildStdin that we're using. */
+ CloseHandle(hChildStdinWr);
+
+ if (!CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &saAttr, 0))
+ return false;
+
+ fSuccess = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd,
+ GetCurrentProcess(), &hChildStdoutRdDup, 0,
+ FALSE, DUPLICATE_SAME_ACCESS);
+ if (!fSuccess) return false;
+ CloseHandle(hChildStdoutRd);
+
+ int hStdIn = _open_osfhandle((long)hChildStdinWrDup, _O_WRONLY|_O_TEXT);
+ FILE *pStdIn = _fdopen(hStdIn, "w");
+ int hStdOut = _open_osfhandle((long)hChildStdoutRdDup, _O_RDONLY|_O_TEXT);
+ FILE *pStdOut = _fdopen(hStdOut, "r");
+
+ StartupInfo.dwFlags = STARTF_USESTDHANDLES;
+ StartupInfo.hStdInput = hChildStdinRd;
+ StartupInfo.hStdOutput = hChildStdoutWr;
+ StartupInfo.hStdError = hChildStdoutWr;
+
+ if (!CreateProcess(NULL,pFullCommand,NULL,NULL,TRUE,CREATE_NO_WINDOW,NULL,curdir::GetCurDir()->GetFullFileName().c_str(),&StartupInfo,&ProcessInfo))
+ {
+ delete[] pFullCommand;
+ cerr << "Error starting command: "<<FullCommandLine<<" : "<<GetLastError()<<endl;
+ if (!IgnoreError)
+ throw(1);
+ }
+ delete[] pFullCommand;
+ if (!CloseHandle(hChildStdinRd)) return false;
+ if (!CloseHandle(hChildStdoutWr)) return false;
+
+ CloseHandle(ProcessInfo.hThread);
+ char Buf[256];
+ int Nbr;
+ while ( (Nbr=fread(Buf,1,sizeof(Buf)-1,pStdOut)) > 0)
+ {
+ if (pOutput)
+ {
+ Buf[Nbr]=0;
+ *pOutput+=Buf;
+ }
+ }
+ WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
+ fclose(pStdIn);
+ fclose(pStdOut);
+ }
+ else
+ {
+ if (!CreateProcess(NULL,pFullCommand,NULL,NULL,TRUE,0,NULL,curdir::GetCurDir()->GetFullFileName().c_str(),&StartupInfo,&ProcessInfo))
+ {
+ delete[] pFullCommand;
+ cerr << "Error starting command: "<<Command<<" : "<<GetLastError()<<endl;
+ if (!IgnoreError)
+ throw(1);
+ }
+ delete[] pFullCommand;
+ CloseHandle(ProcessInfo.hThread);
+ WaitForSingleObject(ProcessInfo.hProcess,INFINITE);
+ }
+ DWORD ExitCode=0;
+ if (!GetExitCodeProcess(ProcessInfo.hProcess,&ExitCode) || ExitCode)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error running command: "<<Command<<", but ignoring error\n";
+ return true; // Ignore error
+ }
+ else
+ return false;
+ }
+ CloseHandle(ProcessInfo.hProcess);
+ return true;
+#else
+ string FullCommandLine=Command+Params;
+ int pipeto[2]; /* pipe to feed the exec'ed program input */
+ int pipefrom[2]; /* pipe to get the exec'ed program output */
+
+ if (pOutput || g_Quiet)
+ {
+ pipe( pipeto );
+ pipe( pipefrom );
+ }
+
+ pid_t ID=vfork();
+ if (ID==-1)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error forking when try to run command: "<<Command<<", but ignoring error\n";
+ return true; // Ignore error
+ }
+ else
+ return false;
+ }
+ else if (ID==0)
+ {
+ int argc;
+ const char **pargv;
+
+ if (pOutput || g_Quiet)
+ {
+ dup2( pipeto[0], STDIN_FILENO );
+ dup2( pipefrom[1], STDOUT_FILENO );
+ /* close unnecessary pipe descriptors for a clean environment */
+ close( pipeto[0] );
+ close( pipeto[1] );
+ close( pipefrom[0] );
+ close( pipefrom[1] );
+ }
+
+ poptParseArgvString(FullCommandLine.c_str(), &argc, &pargv);
+ execv(pargv[0],(char *const*)pargv);
+ _exit (EXIT_FAILURE);
+ }
+ else
+ {
+ if (pOutput || g_Quiet)
+ {
+ /* Close unused pipe ends. This is especially important for the
+ * pipefrom[1] write descriptor, otherwise readFromPipe will never
+ * get an EOF. */
+ close( pipeto[0] );
+ close( pipefrom[1] );
+
+ pid_t ID2=vfork();
+ if (ID2==-1)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error forking when try to run command: "<<Command<<", but ignoring error\n";
+ return true; // Ignore error
+ }
+ else
+ return false;
+ }
+ else if (ID2==0)
+ {
+ /* Close pipe write descriptor, or we will never know when the
+ * writer process closes its end of the pipe and stops feeding the
+ * exec'ed program. */
+ close( pipeto[1] );
+ char Buf[256];
+ int Nbr;
+ while ( (Nbr=read(pipefrom[0],Buf,sizeof(Buf)-1)) > 0)
+ {
+ if (pOutput)
+ {
+ Buf[Nbr]=0;
+ *pOutput+=Buf;
+ }
+ }
+ close( pipefrom[0]);
+ _exit (EXIT_FAILURE);
+ }
+ else
+ {
+ /* close unused pipe end */
+ close( pipefrom[0] );
+ close( pipeto[1] );
+
+ int Status;
+ waitpid(ID2,&Status,0); // Wait until the reading of the output is finished
+ }
+ }
+ int Status;
+ int Ret=waitpid(ID,&Status,0);
+ if (Ret!=ID || Status)
+ {
+ if (IgnoreError)
+ {
+ cerr << "Error running command: "<<Command<<", but ignoring error\n";
+ return true; // Ignore error
+ }
+ else
+ return false;
+ }
+ }
+ return true;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+#ifndef WIN32
+string EscapeQuotes(const string &Params)
+{
+ int OldPos=0;
+ int Pos;
+ string Quote("\\\"");
+ string SemiColon(" ; ");
+ string Ret;
+
+ while (1)
+ {
+ int Pos=Params.find_first_of('"',OldPos);
+ int Pos2=Params.find(" & ",OldPos);
+ string ToReplace(Quote);
+ int Inc=1;
+
+ if (Pos==string::npos)
+ {
+ if (Pos2==string::npos)
+ break;
+ Pos=Pos2;
+ ToReplace=SemiColon;
+ Inc=3;
+ }
+ else
+ {
+ if (Pos2!=string::npos && Pos2<Pos)
+ {
+ Pos=Pos2;
+ ToReplace=SemiColon;
+ Inc=3;
+ }
+ }
+ Ret+=Params.substr(OldPos,Pos-OldPos);
+ Ret+=ToReplace;
+ OldPos=Pos+Inc;
+ }
+ Ret+=Params.substr(OldPos);
+ return Ret;
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::ExecuteCommand(string Command,string *pOutput)
+{
+ bool Echo=true;
+ bool IgnoreError=false;
+ while (1)
+ {
+ if (Command[0]=='@')
+ {
+ Echo=false;
+ Command=Command.substr(1);
+ continue;
+ }
+ if (Command[0]=='-')
+ {
+ IgnoreError=true;
+ Command=Command.substr(1);
+ continue;
+ }
+ break;
+ }
+ string InCommand=Command;
+ if (g_Quiet)
+ Echo=false;
+
+ const char *pCom=Command.c_str();
+ int StartCommandPos;
+ int EndCommandPos;
+ int BeginParamPos;
+ if (*pCom=='"')
+ {
+ StartCommandPos=1;
+ EndCommandPos=1;
+ while (pCom[EndCommandPos]!='"') EndCommandPos++;
+ BeginParamPos=EndCommandPos+1;
+ }
+ else
+ {
+ StartCommandPos=0;
+ EndCommandPos=0;
+ while (!strchr(" \t",pCom[EndCommandPos])) EndCommandPos++;
+ BeginParamPos=EndCommandPos;
+ }
+ string Params=Command.substr(BeginParamPos);
+ Command=Command.substr(StartCommandPos,EndCommandPos-StartCommandPos);
+
+ // If we have special characters in the params we always call the command via the shell
+ unsigned i;
+ for (i=0; i<Params.size(); i++)
+ {
+ if (strchr("<>|&",Params[i]))
+ {
+ break;
+ }
+ }
+ if (i==Params.size())
+ {
+ if (Command!="del" && Command!="touch" && Command!="copy" && Command!="echo")
+ Command=GetFullCommand(Command);// + Params;
+#ifndef WIN32
+ if (Command.substr(0,GetComspec().size())==GetComspec())
+ {
+ Params=EscapeQuotes(Params);
+ Params+="\"";
+ }
+#endif
+ }
+ else
+ {
+ if (Command!="echo")
+ {
+ string FullCommand=GetFullCommand(Command);
+ string ComSpec=GetComspec();
+ if (FullCommand.substr(0,ComSpec.size())!=ComSpec)
+ Command=FullCommand; // Only use FullCommand when it was found and not prepending by the comspec.
+ Command=ComSpec+Command;
+#ifndef WIN32
+ Params=EscapeQuotes(Params);
+ Params+="\"";
+#endif
+ }
+ }
+ if (Echo
+ #ifdef _DEBUG
+ || g_DoNotExecute
+ #endif
+ )
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << Command << Params << endl;
+ else
+ cout << InCommand << endl;
+ #endif
+ }
+
+ /* first we check special internal commands */
+ #ifdef _DEBUG
+ if (pOutput || !g_DoNotExecute)
+ {
+ #endif
+ if (Command=="del")
+ {
+ return DeleteFiles(Params);
+ }
+ else if (Command=="touch")
+ {
+ return TouchFiles(Params);
+ }
+ else if (Command=="copy")
+ {
+ return CopyFiles(Params);
+ }
+ else if (Command=="echo")
+ {
+ return EchoCommand(Params);
+ }
+
+ return OsExeCommand(Command,Params,IgnoreError,pOutput);
+ #ifdef _DEBUG
+ }
+ #endif
+ return true; /* No Error */
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::BuildDependencies(const refptr<rule> &pRule, const refptr<fileinfo> &Target, mh_time_t TargetDate, mh_time_t &YoungestDate, bool &MakeTarget)
+{
+
+ vector< refptr<fileinfo> > &Deps=Target->GetDeps();
+ vector< refptr<fileinfo> >::iterator pDepIt=Deps.begin();
+ while (pDepIt!=Deps.end())
+ {
+ mh_time_t DepDate=BuildTarget(*pDepIt);
+ if (DepDate.IsNewer(YoungestDate))
+ YoungestDate=DepDate;
+ if (DepDate.IsNewer(TargetDate))
+ {
+ #ifdef _DEBUG
+ if (pRule&&g_pPrintDependencyCheck && DepDate.IsExistingFile() && TargetDate.IsExistingFile())
+ cout<<"Going to build "<<Target->GetFullFileName()<<" because "<<(*pDepIt)->GetFullFileName()<<" is more recent\n";
+ #endif
+ MakeTarget=true;
+ }
+ pDepIt++;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+mh_time_t mhmakefileparser::BuildTarget(const refptr<fileinfo> &Target,bool bCheckTargetDir)
+{
+ #ifdef _DEBUG
+ if (g_CheckCircularDeps)
+ {
+ deque< refptr<fileinfo> >::const_iterator pFind=find(m_TargetStack.begin(),m_TargetStack.end(),Target);
+ if (pFind!=m_TargetStack.end())
+ {
+ cout << "Circular dependency detected.\n"<<Target->GetFullFileName()<<" depending on itself.\n";
+ cout << "Dependency stack:\n";
+ deque< refptr<fileinfo> >::const_iterator pIt=m_TargetStack.begin();
+ while (pIt!=m_TargetStack.end())
+ {
+ cout << " " << (*pIt)->GetFullFileName() << endl;
+ pIt++;
+ }
+ }
+ if (!Target->IsBuild()) m_TargetStack.push_back(Target);
+ }
+ #endif
+
+ #ifdef _DEBUG
+ static int Indent;
+ #endif
+
+ if (Target->IsBuild())
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<" Already build "<<Target->GetFullFileName()<<" : "<<Target->GetDate()<<endl;
+ }
+ #endif
+ return Target->GetDate();
+ }
+
+ #ifdef _DEBUG
+ if (g_GenProjectTree)
+ cout << Target->GetFullFileName() << endl;
+
+ Indent++;
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"Building dependencies of "<<Target->GetFullFileName()<<endl;
+ }
+ #endif
+
+ Target->SetBuild();
+
+ /* Optimisation: do not build target when target dir does not exist,
+ but first build the target dir, in case there exists a rule for it*/
+ refptr<rule> pRule=Target->GetRule();
+
+ if (!pRule && bCheckTargetDir)
+ {
+ refptr<fileinfo> TargetDir=Target->GetDir();
+ mh_time_t TargetDirDate=BuildTarget(TargetDir,false);
+
+ if (!TargetDir->Exists())
+ {
+ #ifdef _DEBUG
+ Indent--;
+ if (g_CheckCircularDeps)
+ {
+ m_TargetStack.pop_back();
+ }
+ #endif
+ return TargetDirDate;
+ }
+ }
+
+ mh_time_t TargetDate=Target->GetDate();
+ bool MakeTarget=false;
+ mh_time_t YoungestDate=TargetDate;
+
+ if (!pRule || !pRule->GetCommands().size())
+ {
+ vector< pair<refptr<fileinfo>,refptr<rule> > > Result;
+
+
+ IMPLICITRULE::SearchImplicitRule(Target,Result);
+
+ vector< pair<refptr<fileinfo>,refptr<rule> > >::iterator ResultIt=Result.begin();
+ while (ResultIt!=Result.end())
+ {
+ if (ResultIt->first==NullFileInfo)
+ {
+ pRule=ResultIt->second;
+ Target->SetRule(pRule);
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Found implicit rule for "<<Target->GetFullFileName()<<endl;
+ pRule->PrintCommands(Target);
+ }
+ #endif
+ break;
+ }
+ else
+ {
+ #ifdef _DEBUG
+ m_ImplicitSearch++;
+ #endif
+ mh_time_t DepDate=BuildTarget(ResultIt->first);
+ #ifdef _DEBUG
+ m_ImplicitSearch--;
+ #endif
+ if (DepDate.DoesExist()) {
+ if (DepDate.IsNewer(YoungestDate))
+ YoungestDate=DepDate;
+ pRule=ResultIt->second;
+ Target->AddMainDep(ResultIt->first);
+ Target->SetRule(pRule); /* This is an implicit rule so do not add the target */
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Found implicit rule for "<<Target->GetFullFileName()<<". Dependent "<<ResultIt->first->GetFullFileName()<<endl;
+ pRule->PrintCommands(Target);
+ }
+ #endif
+ if (DepDate.IsNewer(TargetDate))
+ {
+ #ifdef _DEBUG
+ if (pRule,g_pPrintDependencyCheck && DepDate.IsExistingFile() && TargetDate.IsExistingFile())
+ cout<<"Going to build "<<Target->GetFullFileName()<<" because "<<ResultIt->first->GetFullFileName()<<" is more recent\n";
+ #endif
+ MakeTarget=true;
+ }
+ break;
+ }
+ }
+ ResultIt++;
+ }
+ }
+
+ mhmakeparser *pMakefile=NULL;
+ if (pRule)
+ {
+ pMakefile=pRule->GetMakefile();
+ if (pMakefile->ForceAutoDepRescan()||MakeTarget==true)
+ pMakefile->UpdateAutomaticDependencies(Target);
+ }
+
+ BuildDependencies(pRule,Target,TargetDate,YoungestDate,MakeTarget);
+
+ if (pRule)
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"Building "<<Target->GetFullFileName()<<endl;
+ }
+ #endif
+ if (!MakeTarget)
+ {
+ if (!TargetDate.DoesExist() || ( (g_RebuildAll || pMakefile->m_RebuildAll) && TargetDate.IsOlder(m_sBuildTime)))
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ if (!TargetDate.DoesExist())
+ {
+ if (!m_ImplicitSearch && !Target->IsPhony())
+ cout<<"Building "<<Target->GetFullFileName()<<" because it does not exist yet\n";
+ }
+ else if (TargetDate.IsOlder(m_sBuildTime))
+ {
+ cout<<"Building "<<Target->GetFullFileName()<<" because need to rebuild all (-a)\n";
+ }
+ }
+ #endif
+ MakeTarget=true;
+ }
+ }
+
+ // Now execute the commands
+ vector<string> &Commands=pRule->GetCommands();
+
+ while (1)
+ {
+ vector<string>::iterator CommandIt=Commands.begin();
+ if (MakeTarget)
+ {
+ pMakefile->UpdateAutomaticDependencies(Target);
+ BuildDependencies(pRule,Target,TargetDate,YoungestDate,MakeTarget); /* Since it could be that there are dependencies added, make sure that they are all build before building this target */
+
+ pMakefile->InitEnv(); // Make sure that the exports are set in the evironment
+#ifdef _DEBUG
+ if (!g_GenProjectTree)
+#endif
+ cout << "Building " << Target->GetFullFileName()<<endl;
+ }
+
+ curdir::ChangeCurDir(pMakefile->GetMakeDir());
+
+ md5_context ctx;
+ md5_starts( &ctx );
+ while (CommandIt!=Commands.end())
+ {
+ pMakefile->SetRuleThatIsBuild(Target); // Make sure that the command expension is correct
+ string Command=pMakefile->ExpandExpression(*CommandIt);
+ pMakefile->ClearRuleThatIsBuild(); /* Make sure that further expansion is not taking this rule into account.*/
+ md5_update( &ctx, (uint8 *)Command.c_str(), (unsigned long)Command.size());
+ if (MakeTarget)
+ {
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"-> "<<Command<<endl;
+ }
+ if (!g_GenProjectTree)
+ #endif
+ if (!pMakefile->ExecuteCommand(Command))
+ {
+ cerr << "Error running command: "<<Command<<endl;
+ cerr << "Command defined in makefile: "<<GetMakeDir()->GetFullFileName()<<endl;
+ Target->SetCommandsMd5_32(0); /* Clear the md5 to make sure that the target is rebuild the next time mhmake is ran */
+ m_AutoDepsDirty=true; /* We need to update the autodeps file if the md5 has been changed */
+ throw(1);
+ }
+ }
+ CommandIt++;
+ }
+
+ uint32 Md5_32=md5_finish32( &ctx);
+ if (MakeTarget)
+ {
+ #ifdef _DEBUG
+ if (g_DoNotExecute||g_GenProjectTree)
+ Target->SetDateToNow();
+ else
+ #endif
+ Target->InvalidateDate();
+ mh_time_t NewDate=Target->GetDate();
+ if (NewDate.IsNewer(YoungestDate))
+ YoungestDate=NewDate;
+
+ Target->SetCommandsMd5_32(Md5_32); /* If the rule of the target was added with an implicit rule the targets in the rule is empty */
+ pMakefile->AddTarget(Target);
+ pRule->SetTargetsIsBuild(Md5_32);
+ break;
+ }
+ else if (!Target->CompareMd5_32(Md5_32))
+ {
+ if (TargetDate.IsNewerOrSame(m_sBuildTime) || TargetDate.IsDir())
+ {
+ // Only rebuild if it is not yet rebuild in this current run. This may happen for implicit rules that have multiple targets (implicit rules that build more then one target at the same time
+ Target->SetCommandsMd5_32(Md5_32);
+ pMakefile->AddTarget(Target);
+ m_AutoDepsDirty=true; /* We need to update the autodeps file if the md5 has been changed */
+ break;
+ }
+ else
+ {
+ #ifdef _DEBUG
+ if (!g_GenProjectTree)
+ cout << "Md5 is different for " << Target->GetFullFileName() << " Old:"<<hex<<Target->GetCommandsMd5_32()<<", New: "<<Md5_32<<". Commandline must have been changed so recompiling\n";
+ #endif
+ MakeTarget=true;
+ }
+ }
+ else
+ break;
+ }
+ }
+
+ #ifdef _DEBUG
+ if (g_pPrintDependencyCheck)
+ {
+ for (int i=0; i<Indent; i++)
+ cout<<" ";
+ cout<<"Building "<<Target->GetFullFileName()<<" finished : "<< YoungestDate << endl;
+ }
+ Indent--;
+ if (g_CheckCircularDeps)
+ {
+ m_TargetStack.pop_back();
+ }
+
+ if (!m_ImplicitSearch && !Target->Exists() && !Target->IsPhony() && !g_DoNotExecute && !g_GenProjectTree)
+ {
+ // This is only a warning for phony messages
+ cout<<"Warning: don't know how to make "<<Target->GetFullFileName()<<"\nMake the rule a phony rule to avoid this warning (but only do it when it is really phony).\n";;
+ }
+ #endif
+ Target->SetDate(YoungestDate); /* This is especially needed for phony targets in between real targets */
+ return YoungestDate;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::BuildIncludedMakefiles()
+{
+ vector< refptr<fileinfo> >::iterator MakefileIt=m_IncludedMakefiles.begin();
+ while (MakefileIt!=m_IncludedMakefiles.end())
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout<<"Building include file "<<(*MakefileIt)->GetFullFileName()<<endl;
+ #endif
+ BuildTarget(*MakefileIt);
+ MakefileIt++;
+ }
+}
diff --git a/tools/mhmake/src/curdir.cpp b/tools/mhmake/src/curdir.cpp
new file mode 100644
index 000000000..bdaf444b5
--- /dev/null
+++ b/tools/mhmake/src/curdir.cpp
@@ -0,0 +1,61 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "fileinfo.h"
+#include "curdir.h"
+#include "util.h"
+
+set<refptr<fileinfo>,less_refptrfileinfo> g_FileInfos; // declare here since it is important that it is constructed before m_pcurrentdir
+curdir::initcurdir curdir::m_pCurrentDir;
+
+///////////////////////////////////////////////////////////////////////////////
+curdir::initcurdir &curdir::initcurdir::operator=(const refptr<fileinfo>& Src)
+{
+ return (curdir::initcurdir&)refptr<fileinfo>::operator=(Src);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+curdir::initcurdir::initcurdir()
+{
+ char CurDir[MAX_PATH];
+ getcwd(CurDir,MAX_PATH);
+ *this=GetFileInfo(CurDir,refptr<fileinfo>());
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void curdir::ChangeCurDir(const refptr<fileinfo>&NewDir)
+{
+ if (NewDir!=m_pCurrentDir)
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Changing to dir "<<NewDir->GetFullFileName()<<endl;
+ #endif
+ if (-1==chdir(NewDir->GetFullFileName().c_str()))
+ {
+ cerr<<"Error changing to directory "<<NewDir->GetFullFileName()<<endl;
+ throw(1);
+ }
+ m_pCurrentDir=NewDir;
+ }
+}
+
diff --git a/tools/mhmake/src/curdir.h b/tools/mhmake/src/curdir.h
new file mode 100644
index 000000000..1dad43457
--- /dev/null
+++ b/tools/mhmake/src/curdir.h
@@ -0,0 +1,49 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __CURDIR_H
+#define __CURDIR_H
+
+#include "refptr.h"
+class fileinfo;
+
+class curdir
+{
+public:
+ class initcurdir : public refptr<fileinfo>
+ {
+ public:
+ initcurdir &operator=(const refptr<fileinfo>& Src);
+
+ initcurdir();
+ };
+private:
+ static initcurdir m_pCurrentDir;
+
+public:
+ static refptr<fileinfo> &GetCurDir()
+ {
+ return m_pCurrentDir;
+ }
+ static void ChangeCurDir(const refptr<fileinfo>&NewDir);
+};
+
+#endif
+
diff --git a/tools/mhmake/src/fileinfo.cpp b/tools/mhmake/src/fileinfo.cpp
new file mode 100644
index 000000000..65b7d52a2
--- /dev/null
+++ b/tools/mhmake/src/fileinfo.cpp
@@ -0,0 +1,339 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "fileinfo.h"
+#include "rule.h"
+#include "util.h"
+#include "mhmakeparser.h"
+
+const string NullString;
+refptr<fileinfo> NullFileInfo;
+
+#ifdef WIN32
+ZEROTIME g_ZeroTime;
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+refptr<fileinfo> fileinfo::GetDir() const
+{
+ return GetAbsFileInfo(m_AbsFileName.substr(0,m_AbsFileName.find_last_of(OSPATHSEP)));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string fileinfo::GetName() const
+{
+ return m_AbsFileName.substr(m_AbsFileName.find_last_of(OSPATHSEP)+1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+mh_time_t fileinfo::realGetDate()
+{
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ HANDLE hFind=FindFirstFile(m_AbsFileName.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ m_Date.SetNotExist();
+ }
+ else
+ {
+ FindClose(hFind);
+ if (FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ { // For directories we just take an old time since the lastwritetime is changed each time something
+ // is added to the directory
+ m_Date.SetDir();
+ }
+ else
+ {
+ m_Date=g_ZeroTime.ConvertTime(&FindData.ftLastWriteTime);
+ }
+ }
+#else
+ struct stat Buf;
+ if (-1==stat(m_AbsFileName.c_str(),&Buf))
+ m_Date.SetNotExist();
+ else if (S_ISDIR(Buf.st_mode))
+ m_Date.SetDir();
+ else
+ m_Date=Buf.st_mtime;
+#endif
+ return m_Date;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool fileinfo::IsDir() const
+{
+#ifdef WIN32
+ WIN32_FIND_DATA FindData;
+ HANDLE hFind=FindFirstFile(m_AbsFileName.c_str(),&FindData);
+ if (hFind==INVALID_HANDLE_VALUE)
+ {
+ return false;
+ }
+ else
+ {
+ FindClose(hFind);
+ if (FindData.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY)
+ {
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+ }
+#else
+ struct stat Buf;
+ if (-1==stat(m_AbsFileName.c_str(),&Buf))
+ return false; // File does not exist, so consider this as not a directory
+ else
+ return 0!=S_ISDIR (Buf.st_mode);
+ return true;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void fileinfo::SetDateToNow()
+{
+#ifdef WIN32
+ FILETIME FileTime;
+ GetSystemTimeAsFileTime(&FileTime);
+ m_Date=g_ZeroTime.ConvertTime(&FileTime);
+#else
+ m_Date=time(NULL);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string fileinfo::GetPrerequisits() const
+{
+ // Build a string with all prerequisits, but make sure that every dependency
+ // is only in there once (we do this be building a set in parallel
+ vector< refptr<fileinfo> >::const_iterator DepIt=m_Deps.begin();
+ set< refptr<fileinfo> > Deps;
+ bool first=true;
+ string Ret=g_EmptyString;
+ while (DepIt!=m_Deps.end())
+ {
+ set< refptr<fileinfo> >::iterator pFound=Deps.find(*DepIt);
+ if (pFound==Deps.end())
+ {
+ if (first)
+ {
+ first=false;
+ }
+ else
+ {
+ Ret+=g_SpaceString;
+ }
+ Ret+=(*DepIt)->GetFullFileName();
+ }
+ Deps.insert(*DepIt);
+ DepIt++;
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void fileinfo::AddDeps(vector< refptr<fileinfo> > &Deps)
+{
+ vector< refptr<fileinfo> >::iterator It=Deps.begin();
+ vector< refptr<fileinfo> >::iterator ItEnd=Deps.end();
+ while (It!=ItEnd)
+ {
+ AddDep(*It++);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool fileinfo::IsAutoDepExtention(void) const
+{
+ const char *pName=GetFullFileName().c_str();
+ const char *pExt=strrchr(pName,'.');
+ if (!pExt)
+ return false;
+ pExt++;
+ if (m_pRule)
+ {
+ string ObjExt=m_pRule->GetMakefile()->ExpandVar(OBJEXTVAR);
+ return ((0==strcmp(pExt,ObjExt.c_str()+1)) || (0==strcmp(pExt,"h")));
+ }
+ else
+ return ((0==strcmp(pExt,"obj")) || (0==strcmp(pExt,"doj")) || (0==strcmp(pExt,"o")) || (0==strcmp(pExt,"h")));
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void fileinfo::DumpErrorMessageDuplicateRule(const refptr<rule>&pRule)
+{
+ cerr << m_AbsFileName << ": rule is defined multiple times\n";
+ cerr << "First ("<<m_pRule->GetMakefile()->GetMakeDir()->GetFullFileName()<<") :\n";
+
+ vector<string>::const_iterator It=m_pRule->GetCommands().begin();
+ while (It!=m_pRule->GetCommands().end())
+ {
+ cerr << " " << m_pRule->GetMakefile()->ExpandExpression(*It) << endl;
+ It++;
+ }
+ cerr << "Second ("<<pRule->GetMakefile()->GetMakeDir()->GetFullFileName()<<") :\n";
+ It=pRule->GetCommands().begin();
+ while (It!=pRule->GetCommands().end())
+ {
+ cerr << " " << pRule->GetMakefile()->ExpandExpression(*It) << endl;
+ It++;
+ }
+}
+#endif
+
+///////////////////////////////////////////////////////////////////////////////
+static inline string &GetFullLowerPathName(string &Name)
+{
+ const char *pPtr=Name.c_str();
+ const char *pBeg=pPtr;
+ const char *pLastSlash=NULL;
+ char *pWr=(char*)pBeg;
+ char Char=*pPtr++;
+
+ while (Char)
+ {
+ if (Char=='\\' || Char=='/')
+ {
+ char Char2=pPtr[0];
+ if (Char2=='.')
+ {
+ if (pPtr[1]=='.')
+ {
+ pPtr+=2;
+ while ((pPtr[0]=='\\' || pPtr[0]=='/') && pPtr[1]=='.' && pPtr[2]=='.')
+ {
+ pLastSlash--;
+ while (*pLastSlash!='\\' && *pLastSlash!='/') pLastSlash--;
+ if (pLastSlash<pBeg)
+ pLastSlash=pBeg; // This is a fault in the file name, just put it back at the beginning
+ pPtr+=3;
+ }
+ if (pLastSlash)
+ pWr=(char*)pLastSlash;
+ }
+ else
+ {
+ if (pPtr[1]=='\\' || pPtr[1]=='/')
+ {
+ pPtr++;
+ }
+ else
+ {
+ pLastSlash=pWr;
+ *pWr++ = OSPATHSEP;
+ }
+ }
+ }
+ else if (Char2=='\\' || Char2=='/')
+ {
+ }
+ else
+ {
+ pLastSlash=pWr;
+ *pWr++ = OSPATHSEP;
+ }
+ }
+ else
+ {
+ *pWr++ = tolower(Char);
+ }
+ Char=*pPtr++;
+ }
+ *pWr=0;
+ Name.resize(pWr-pBeg);
+
+ return Name;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+const refptr<fileinfo> &GetFileInfo(const string &NameIn,const refptr<fileinfo> &RelDir)
+{
+ string Name=NameIn;
+ bool DoesExist=true;
+ //Only concatenate if szName is not already a full name
+#ifdef WIN32
+ if (!Name.empty() && Name[1]!=':')
+#endif
+ {
+ if (Name[0]!=OSPATHSEP)
+ {
+ Name=RelDir->GetFullFileName()+OSPATHSEPSTR+Name;
+ if (!RelDir->Exists()) /* if the directory does not exist, the file will not exist either */
+ DoesExist=false;
+ }
+ #ifdef WIN32
+ else
+ {
+ /* The filename is absolute but does not contain a driver letter. So add it (only on windows) */
+ Name=RelDir->GetFullFileName().substr(0,2)+Name;
+ }
+ #endif
+ }
+ const refptr<fileinfo> &Ret=GetAbsFileInfo(GetFullLowerPathName(Name));
+ #ifdef _DEBUG
+ if (Ret->GetFullFileName().find(' ')!=string::npos)
+ {
+ cerr << "Spaces are not allowed in filenames: "<< Ret->GetFullFileName() << endl;
+ exit(1);
+ }
+ #endif
+ if (!DoesExist)
+ Ret->SetNotExist();
+ return Ret;
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void PrintFileInfos()
+{
+ set<refptr<fileinfo>,less_refptrfileinfo>::iterator pIt=g_FileInfos.begin();
+ while (pIt!=g_FileInfos.end())
+ {
+ cout<<(*pIt)->GetFullFileName()<<" :";
+ if ((*pIt)->IsPhony())
+ cout<<" (phony)";
+ vector< refptr<fileinfo> > &Deps=(*pIt)->GetDeps();
+ vector< refptr<fileinfo> >::iterator pDepIt=Deps.begin();
+ while (pDepIt!=Deps.end())
+ {
+ cout<<g_SpaceString<<(*pDepIt)->GetFullFileName();
+ pDepIt++;
+ }
+ cout<<endl;
+ // Write the commands
+ refptr<rule> pRule=(*pIt)->GetRule();
+ if (pRule)
+ {
+ cout<<g_SpaceString<<"Run in: "<<pRule->GetMakefile()->GetMakeDir()->GetFullFileName()<<endl;
+ pRule->PrintCommands();
+ }
+ pIt++;
+ }
+
+}
+#endif
+
diff --git a/tools/mhmake/src/fileinfo.h b/tools/mhmake/src/fileinfo.h
new file mode 100644
index 000000000..c44041d30
--- /dev/null
+++ b/tools/mhmake/src/fileinfo.h
@@ -0,0 +1,450 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __FILEINFO_H
+#define __FILEINFO_H
+
+#include "curdir.h"
+#include "rule.h"
+#include "md5.h"
+
+#ifdef WIN32
+#define OSPATHSEP '\\'
+#define OSPATHSEPSTR "\\"
+#define OSPATHENVSEP ';'
+#define OSPATHENVSEPSTR ";"
+#else
+#define OSPATHSEP '/'
+#define OSPATHSEPSTR "/"
+#define OSPATHENVSEP ':'
+#define OSPATHENVSEPSTR ":"
+#endif
+
+extern bool g_DumpOnError;
+extern bool g_PrintVarsAndRules;
+extern bool g_DoNotExecute;
+extern bool g_GenProjectTree;
+extern bool g_Quiet;
+extern bool g_RebuildAll;
+extern bool g_PrintAdditionalInfo;
+extern bool g_pPrintDependencyCheck;
+extern bool g_CheckCircularDeps;
+extern bool g_ForceAutoDepRescan;
+extern bool g_PrintLexYacc;
+extern bool g_Clean;
+extern bool g_PrintMultipleDefinedRules;
+
+extern const string g_EmptyString;
+extern const string g_SpaceString;
+
+#define TIMESAFETY 3
+class mh_time
+{
+ enum
+ {
+ DATENOTVALID=0,
+ NOTEXISTTIME=1,
+ DIRTIME =2+TIMESAFETY
+ };
+ unsigned long m_Time;
+
+ bool operator < (const mh_time &Src);
+public:
+ mh_time(){m_Time=DATENOTVALID;}
+#ifdef WIN32
+ mh_time(unsigned __int64 Time) : m_Time((unsigned long)(Time&0xffffffff)) {}
+ mh_time(__int64 Time) : m_Time((unsigned long)(Time&0xffffffff)) {}
+#endif
+ mh_time(unsigned long Time) : m_Time(Time) {}
+ mh_time(const mh_time &Time) : m_Time(Time.m_Time) {}
+
+ void SetDir(void)
+ {
+ m_Time=DIRTIME;
+ }
+ bool IsDir(void) const
+ {
+ return m_Time==DIRTIME;
+ }
+ void SetNotExist(void)
+ {
+ m_Time=NOTEXISTTIME;
+ }
+ bool IsExistingFile(void) const
+ {
+ return m_Time>DIRTIME;
+ }
+ bool DoesExist(void) const
+ {
+ return m_Time!=NOTEXISTTIME;
+ }
+ void Invalidate(void)
+ {
+ m_Time=DATENOTVALID;
+ }
+ bool IsDateValid(void) const
+ {
+ return m_Time!=DATENOTVALID;
+ }
+ friend ostream& operator<<(ostream& out,const mh_time &Src);
+
+ mh_time &operator = (const mh_time &Src) { m_Time=Src.m_Time; return *this;}
+
+ bool IsOlder(const mh_time &Src) const { return m_Time<Src.m_Time-TIMESAFETY; }
+
+ bool IsNewer(const mh_time &Src) const { return m_Time>Src.m_Time+TIMESAFETY; }
+ bool IsNewerOrSame(const mh_time &Src) const { return m_Time>=Src.m_Time-TIMESAFETY; }
+};
+typedef mh_time mh_time_t;
+
+inline ostream& operator<<(ostream& out,const mh_time &Src)
+{
+ out << hex << (unsigned long)Src.m_Time;
+ return out;
+}
+
+class fileinfo : public refbase
+{
+ string m_AbsFileName;
+ bool m_IsPhony;
+ bool m_IsBuild;
+ refptr<rule> m_pRule;
+ vector< refptr<fileinfo> > m_Deps;
+ mh_time_t m_Date;
+ uint32 m_CommandsMd5_32; // 32-bit Md5 checksum of commands to build this target
+
+ fileinfo(const fileinfo &Src);
+ fileinfo(void);
+public:
+
+ fileinfo(const string &AbsFileName,uint32 Md5_32)
+ {
+ m_IsPhony=false;
+ m_IsBuild=false;
+ m_AbsFileName=AbsFileName;
+ InvalidateDate();
+ m_CommandsMd5_32=Md5_32;
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Initialising Md5 of "<<GetFullFileName().c_str()<<" to 0x"<<hex<<Md5_32<<endl;
+ #endif
+ }
+ fileinfo(const string &AbsFileName)
+ {
+ new (this) fileinfo(AbsFileName,0);
+ }
+ /* The following constructor is only used for name comparisons, and should only be used for that */
+ fileinfo(int Dummy,const string &AbsFileName)
+ {
+ m_AbsFileName=AbsFileName;
+ }
+
+ fileinfo(const char *szFile)
+ {
+ new (this) fileinfo(string(szFile));
+ }
+ fileinfo(const char *szFile,uint32 Md5_32)
+ {
+ new (this) fileinfo(string(szFile),Md5_32);
+ }
+ ~fileinfo()
+ {
+ }
+ const string &GetFullFileName(void) const
+ {
+ return m_AbsFileName;
+ }
+ void SetFullFileName(const string &strAbsName)
+ {
+ m_AbsFileName=strAbsName;
+ // If the last char is path sep strip it
+ if (!m_AbsFileName.empty() && m_AbsFileName[m_AbsFileName.length()-1]==OSPATHSEP)
+ m_AbsFileName.resize(m_AbsFileName.length()-1);
+ }
+ fileinfo &operator = (const fileinfo &Src)
+ {
+ new (this) fileinfo(Src);
+ return *this;
+ }
+
+ refptr<fileinfo> GetDir(void) const;
+ string GetName() const;
+ bool IsDir() const;
+
+ void DumpErrorMessageDuplicateRule(const refptr<rule> &pRule);
+
+ void SetRule(refptr<rule> &pRule)
+ {
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ if (m_pRule && m_pRule->GetCommands().size()) {
+ _asm int 3;
+ }
+ #endif
+ m_pRule=pRule;
+ }
+
+ void SetRuleIfNotExist(refptr<rule> &pRule)
+ {
+ if (pRule)
+ {
+ if (!m_pRule)
+ {
+ SetRule(pRule);
+ pRule->AddTarget(this);
+ }
+ #ifdef _DEBUG
+ else
+ {
+ if (*m_pRule!=*pRule)
+ {
+ DumpErrorMessageDuplicateRule(pRule);
+ throw(1);
+ }
+ else if (g_PrintMultipleDefinedRules)
+ {
+ DumpErrorMessageDuplicateRule(pRule);
+ }
+ }
+ #endif
+ }
+ }
+
+ refptr<rule> GetRule(void)
+ {
+ return m_pRule;
+ }
+ void AddDep(const refptr<fileinfo> &Dep)
+ {
+ if (&*Dep==this)
+ {
+ #ifdef _DEBUG
+ cout << GetFullFileName()<<" is directly dependent on itself\n";
+ #endif
+ return;
+ }
+ m_Deps.push_back(Dep);
+ }
+ void AddDeps(vector< refptr<fileinfo> > &Deps);
+
+ void InsertDeps(vector< refptr<fileinfo> > &Deps)
+ {
+ vector< refptr<fileinfo> > NewDeps;
+ vector< refptr<fileinfo> >::const_iterator It=Deps.begin();
+ vector< refptr<fileinfo> >::const_iterator ItEnd=Deps.end();
+ while (It!=ItEnd)
+ {
+ if (&**It==this)
+ {
+ #ifdef _DEBUG
+ cout << GetFullFileName()<<" is directly dependent on itself\n";
+ #endif
+ }
+ else
+ NewDeps.push_back(*It);
+ It++;
+ }
+ if (NewDeps.size())
+ m_Deps.insert(m_Deps.begin(),NewDeps.begin(),NewDeps.end());
+ }
+ void AddMainDep(refptr<fileinfo> &MainDep)
+ {
+ if (&*MainDep==this)
+ {
+ #ifdef _DEBUG
+ cout << GetFullFileName()<<" is directly dependent on itself\n";
+ #endif
+ return;
+ }
+ m_Deps.insert(m_Deps.begin(),MainDep);
+ }
+ vector< refptr<fileinfo> > &GetDeps(void)
+ {
+ return m_Deps;
+ }
+ string GetPrerequisits(void) const;
+ void SetPhony(void)
+ {
+ m_IsPhony=true;
+ m_Date.SetNotExist(); // This will sure that this target will always be build (even if a corresponding file exists)
+ }
+ bool IsPhony(void)
+ {
+ return m_IsPhony;
+ }
+ mh_time_t realGetDate(void);
+ void SetDateToNow(void);
+
+ void SetDate(mh_time_t Date)
+ {
+ m_Date=Date;
+ }
+
+ bool IsDateValid() const
+ {
+ return m_Date.IsDateValid();
+ }
+ void InvalidateDate(void)
+ {
+ m_Date.Invalidate();
+ }
+
+ mh_time_t GetDate(void)
+ {
+ if (m_Date.IsDateValid())
+ return m_Date;
+ else
+ return realGetDate();
+ }
+ void SetNotExist(void)
+ { // this is used to make sure that this item is rebuild, even if it really exists
+ m_Date.SetNotExist();
+ }
+ bool Exists(void)
+ {
+ return GetDate().DoesExist();
+ }
+ bool IsBuild(void) const
+ {
+ return m_IsBuild;
+ }
+ void SetBuild(void)
+ {
+ m_IsBuild=true;
+ }
+ void ClearBuild(void)
+ {
+ m_IsBuild=false;
+ }
+ bool IsAutoDepExtention(void) const;
+
+ void SetCommandsMd5_32(uint32 Md5_32)
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Setting Md5 of "<<GetFullFileName().c_str()<<" to 0x"<<hex<<Md5_32<<endl;
+ #endif
+ m_CommandsMd5_32=Md5_32;
+ }
+#ifdef _DEBUG
+ uint32 GetCommandsMd5_32(void) const
+ {
+ return m_CommandsMd5_32;
+ }
+#endif
+ bool CompareMd5_32(uint32 Md5_32) const
+ {
+ return m_CommandsMd5_32==Md5_32;
+ }
+ void WriteMd5_32(FILE *pFile) const
+ {
+ fwrite(&m_CommandsMd5_32,sizeof(m_CommandsMd5_32),1,pFile);
+ }
+};
+
+struct less_refptrfileinfo : public binary_function <refptr<fileinfo>, refptr<fileinfo>, bool>
+{
+ bool operator()(const refptr<fileinfo>& _Left, const refptr<fileinfo>& _Right) const
+ {
+ return less<string>().operator ()(_Left->GetFullFileName(),_Right->GetFullFileName());
+ }
+};
+
+struct less_fileinfo : public binary_function <const fileinfo*, const fileinfo*, bool>
+{
+ bool operator()(const fileinfo *_Left, const fileinfo *_Right) const
+ {
+ return less<string>().operator ()(_Left->GetFullFileName(),_Right->GetFullFileName());
+ }
+};
+
+extern const string NullString;
+extern refptr<fileinfo> NullFileInfo;
+
+const refptr<fileinfo> &GetFileInfo(const string &szName,const refptr<fileinfo> &pRelDir=curdir::GetCurDir());
+
+extern set<refptr<fileinfo>,less_refptrfileinfo > g_FileInfos;
+
+inline const refptr<fileinfo> &GetAbsFileInfo(const string &strAbsName)
+{
+ static refptr<fileinfo> SearchFileInfo(new fileinfo(""));
+ SearchFileInfo->SetFullFileName(strAbsName);
+ /* Using find is just an optimalisation, you could use insert immediately */
+ set<refptr<fileinfo>,less_refptrfileinfo >::const_iterator pFind=g_FileInfos.find(SearchFileInfo);
+ if (pFind==g_FileInfos.end())
+ {
+ pair <set<refptr<fileinfo>,less_refptrfileinfo >::iterator, bool> pPair=g_FileInfos.insert(new fileinfo(SearchFileInfo->GetFullFileName()));
+ return *(pPair.first);
+ }
+ else
+ return *pFind;
+}
+
+
+inline const refptr<fileinfo> &GetFileInfo(const string &szName,const string &RelDir)
+{
+ return GetFileInfo(szName,GetFileInfo(RelDir));
+}
+
+inline const refptr<fileinfo> &GetFileInfo(const char *szName,const char *RelDir)
+{
+ return GetFileInfo(string(szName),string(RelDir));
+}
+
+inline const refptr<fileinfo> &GetFileInfo(const char *szName,const refptr<fileinfo> &RelDir=curdir::GetCurDir())
+{
+ return GetFileInfo(string(szName),RelDir);
+}
+
+void PrintFileInfos();
+
+#ifdef WIN32
+class ZEROTIME
+{
+ __int64 m_ZeroTime;
+public:
+ ZEROTIME()
+ {
+ SYSTEMTIME SystemTime;
+
+ memset(&SystemTime,0,sizeof(SystemTime));
+ SystemTime.wYear=1970;
+ SystemTime.wMonth=1;
+ SystemTime.wDay=1;
+ SystemTime.wDayOfWeek=4;
+
+ SystemTimeToFileTime(&SystemTime,(FILETIME*)&m_ZeroTime);
+ }
+
+ __int64 GetZeroTime()
+ {
+ return m_ZeroTime;
+ }
+
+ mh_time_t ConvertTime(FILETIME *pFileTime)
+ {
+ return (mh_time_t)((*(__int64*)pFileTime-m_ZeroTime)/10000000); /* filetime is in nano seconds*/
+ }
+};
+
+extern ZEROTIME g_ZeroTime;
+#endif
+
+#endif
+
diff --git a/tools/mhmake/src/flexskel.cc b/tools/mhmake/src/flexskel.cc
new file mode 100644
index 000000000..050031b36
--- /dev/null
+++ b/tools/mhmake/src/flexskel.cc
@@ -0,0 +1,1056 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* A lexical scanner generated by flex */
+/* scanner skeleton version:
+ * $Header: c:\\Program\040Files\\Development\\CVS\040Repository/flex++/flexskel.cc,v 1.1.1.1 2002/04/13 06:01:32 Bear Exp $
+ */
+/* MODIFIED FOR C++ CLASS BY Alain Coetmeur: coetmeur(at)icdc.fr */
+/* Note that (at) mean the 'at' symbol that I cannot write */
+/* because it is expanded to the class name */
+/* made at Informatique-CDC, Research&development department */
+/* company from the Caisse Des Depots et Consignations */
+/* institutional financial group */
+
+/* theses symbols are added before this file */
+/* #define YY_CHAR 'unsigned char' if 8bit or 'char' if 7bit */
+/* #define FLEX_DEBUG if debug mode */
+#define FLEX_SCANNER
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+/* Old MSC, before c7 */
+#ifdef MSDOS
+#ifndef _MSDOS
+#define _MSDOS
+#endif
+#endif
+/* turboc */
+#ifdef __MSDOS__
+#ifndef _MSDOS
+#define _MSDOS
+#endif
+#endif
+
+#ifdef __cplusplus
+#include <stdlib.h>
+#define YY_USE_CONST
+#define YY_USE_PROTOS
+#ifndef YY_USE_CLASS
+#define YY_USE_CLASS
+#endif
+#else /* ! __cplusplus */
+#ifdef __STDC__
+#ifdef __GNUC__
+#include <stddef.h>
+void *malloc( size_t );
+void free( void* );
+int read();
+#else
+#include <stdlib.h>
+#endif /* __GNUC__ */
+#define YY_USE_PROTOS
+#define YY_USE_CONST
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+#ifdef __TURBOC__
+#define YY_USE_CONST
+#endif
+#include <stdio.h>
+
+
+/*********************************************/
+/* COMPILER DEPENDENT MACROS */
+/*********************************************/
+/* use prototypes in function declarations */
+/* the "const" storage-class-modifier is valid */
+#ifndef YY_USE_CONST
+#define const
+#endif
+/* use prototypes in function declarations */
+#ifndef YY_PROTO
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+#endif
+
+
+/*********************/
+/* parameters */
+
+/* amount of stuff to slurp up with each read */
+#ifndef YY_READ_BUF_SIZE
+#define YY_READ_BUF_SIZE 8192
+#endif
+/* size of default input buffer */
+#ifndef YY_BUF_SIZE
+#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2)
+#endif
+
+/***********************************/
+/* to be redefined for application */
+
+/* returned upon end-of-file */
+#define YY_END_TOK 0
+/* no semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#define yyterminate() return ( YY_NULL )
+
+/* code executed at the end of each rule */
+#define YY_BREAK break;
+
+/* #define YY_USER_ACTION */
+/* #define YY_USER_INIT */
+
+
+#ifndef YY_USE_CLASS
+/* copy whatever the last rule matched to the standard output */
+/* cast to (char *) is because for 8-bit chars, yy___text is (unsigned char *) */
+/* this used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite()
+ */
+#define ECHO (void) fwrite( (char *) yy___text, yy___leng, 1, yy___out )
+
+/* gets input and stuffs it into "buf". number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifdef _MSDOS
+#define YY_INPUT(buf,result,max_size) \
+ if ( (result = fread(buf,1,max_size,yy___in)) < 0 ) \
+ YY_FATAL_ERROR( "fread() in flex scanner failed" );
+#else
+#define YY_INPUT(buf,result,max_size) \
+ if ( (result = read( fileno(yy___in), (char *) buf, max_size )) < 0 ) \
+ YY_FATAL_ERROR( "read() in flex scanner failed" );
+
+#endif
+/* report a fatal error */
+
+/* The funky do-while is used to turn this macro definition into
+ * a single C statement (which needs a semi-colon terminator).
+ * This avoids problems with code like:
+ *
+ * if ( something_happens )
+ * YY_FATAL_ERROR( "oops, the something happened" );
+ * else
+ * everything_okay();
+ *
+ * Prior to using the do-while the compiler would get upset at the
+ * "else" because it interpreted the "if" statement as being all
+ * done when it reached the ';' after the YY_FATAL_ERROR() call.
+ */
+
+#define YY_FATAL_ERROR(msg) \
+ do \
+ { \
+ (void) fputs( msg, yy___stderr ); \
+ (void) putc( '\n', yy___stderr ); \
+ exit( 1 ); \
+ } \
+ while ( 0 )
+
+/* default yywrap function - always treat EOF as an EOF */
+#define yywrap() 1
+
+
+/* default declaration of generated scanner - a define so the user can
+ * easily add parameters
+ */
+#define YY_DECL int yylex YY_PROTO(( void ))
+#else
+/* c++ */
+#define ECHO yy___echo()
+#define YY_INPUT(buf,result,max_size) \
+ if ( yy___input((char *)buf, result,max_size) < 0 ) \
+ YY_FATAL_ERROR( "YY_INPUT() in flex scanner failed" );
+
+#define YY_FATAL_ERROR(msg) yy___fatal_error(msg)
+#define yywrap() yy___wrap()
+
+#endif
+/***********************************/
+/* not to be changed */
+#define YY_NULL 0
+#define YY_END_OF_BUFFER_CHAR 0
+/* special action meaning "start processing a new file" */
+#define YY_NEW_FILE yy___newfile
+/* enter a start condition. This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN
+ */
+#define BEGIN yy_start = 1 + 2 *
+
+/* action number for EOF rule of a given start state */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+
+
+
+%% section 1 definitions go here
+
+#define yy___stderr YY_@_ERRFILE
+#define yy___text YY_@_TEXT
+#define yy___leng YY_@_LENG
+#define yy___in YY_@_IN
+#define yy___out YY_@_OUT
+#define yy___newfile \
+ do \
+ { \
+ YY_@_INIT_BUFFER( YY_@_CURRENT_BUFFER, yy___in ); \
+ YY_@_LOAD_BUFFER_STATE(); \
+ } \
+ while ( 0 )
+#if YY_@_DEBUG != 0
+#define yy___flex_debug YY_@_DEBUG_FLAG
+#endif
+
+
+#ifdef YY_USE_CLASS
+
+#define yy___echo YY_@_ECHO
+#define yy___input YY_@_INPUT
+#define yy___fatal_error YY_@_FATAL_ERROR
+#define yy___wrap YY_@_WRAP
+
+#endif
+
+/* done after the current pattern has been matched and before the
+ * corresponding action - sets up yy___text
+ */
+#define YY_DO_BEFORE_ACTION \
+ yy___text = yy_bp; \
+%% code to fiddle yy___text and yy___leng for yymore() goes here
+ yy_hold_char = *yy_cp; \
+ *yy_cp = '\0'; \
+ yy_c_buf_p = yy_cp;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+
+/* return all but the first 'n' matched characters back to the input stream */
+#define yyless(n) \
+ do \
+ { \
+ /* undo effects of setting up yy___text */ \
+ *yy_cp = yy_hold_char; \
+ yy_c_buf_p = yy_cp = yy_bp + n; \
+ YY_DO_BEFORE_ACTION; /* set up yy___text again */ \
+ } \
+ while ( 0 )
+
+#define unput(c) yyunput( c, yy___text )
+
+
+
+struct yy_buffer_state
+{
+ YY_@_IFILE *yy_input_file;
+
+ YY_@_CHAR *yy_ch_buf; /* input buffer */
+ YY_@_CHAR *yy_buf_pos; /* current position in input buffer */
+
+ /* size of input buffer in bytes, not including room for EOB characters */
+ int yy_buf_size;
+
+ /* number of characters read into yy_ch_buf, not including EOB characters */
+ int yy_n_chars;
+
+ int yy_eof_status; /* whether we've seen an EOF on this buffer */
+#define EOF_NOT_SEEN 0
+ /* "pending" happens when the EOF has been seen but there's still
+ * some text process
+ */
+#define EOF_PENDING 1
+#define EOF_DONE 2
+ };
+
+/* we provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state"
+ */
+
+#ifndef YY_USE_CLASS
+
+#if YY_@_DEBUG != 0
+int YY_@_DEBUG_FLAG=YY_@_DEBUG_INIT;
+#endif
+#define YY_CURRENT_BUFFER YY_@_CURRENT_BUFFER
+static YY_BUFFER_STATE YY_@_CURRENT_BUFFER;
+/* yy_hold_char holds the character lost when yy___text is formed */
+static YY_@_CHAR yy_hold_char;
+
+static int yy_n_chars; /* number of characters read into yy_ch_buf */
+
+/* GLOBAL */
+YY_@_CHAR *yy___text;
+int yy___leng;
+
+YY_@_IFILE *yy___in = (YY_@_IFILE *) 0;
+YY_@_OFILE *yy___out = (YY_@_OFILE *) 0;
+
+#ifdef __cplusplus
+static int yyinput YY_PROTO(( void ));
+#else
+static int input YY_PROTO(( void ));
+#endif
+/* these variables are all declared out here so that section 3 code can
+ * manipulate them
+ */
+/* points to current character in buffer */
+static YY_@_CHAR *yy_c_buf_p = (YY_@_CHAR *) 0;
+static int yy_init = 1; /* whether we need to initialize */
+static int yy_start = 0; /* start state number */
+
+/* flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yy___in. A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+static int yy_get_next_buffer YY_PROTO(( void ));
+static void yyunput YY_PROTO(( YY_@_CHAR c, YY_@_CHAR *buf_ptr ));
+
+#else
+/* c++ */
+#ifndef YY_@_ECHO_NOCODE
+void YY_@_CLASS::yy___echo()
+{
+ YY_@_ECHO_CODE
+}
+#endif
+#ifndef YY_@_INPUT_NOCODE
+int YY_@_CLASS::yy___input(char * buffer,int &result,int max_size)
+{
+ YY_@_INPUT_CODE
+}
+#endif
+#ifndef YY_@_FATAL_ERROR_NOCODE
+void YY_@_CLASS::yy___fatal_error(char *msg)
+{
+ YY_@_FATAL_ERROR_CODE
+}
+#endif
+#ifndef YY_@_WRAP_NOCODE
+int YY_@_CLASS::yy___wrap()
+{
+ YY_@_WRAP_CODE
+}
+#endif
+void YY_@_CLASS::yy_initialize()
+{
+ yy___in=0;yy___out=0;yy_init = 1;
+ yy_start=0;
+ yy___text=0;yy___leng=0;
+ YY_@_CURRENT_BUFFER=0;
+ yy_did_buffer_switch_on_eof=0;
+ yy_c_buf_p=0;yy_hold_char=0;yy_n_chars=0;
+#if YY_@_DEBUG != 0
+ YY_@_DEBUG_FLAG=YY_@_DEBUG_INIT;
+#endif
+}
+
+YY_@_CLASS::YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM) YY_@_CONSTRUCTOR_INIT
+{
+ yy_initialize();
+ YY_@_CONSTRUCTOR_CODE;
+}
+YY_@_CLASS::~YY_@_CLASS()
+{
+ YY_@_DESTRUCTOR_CODE;
+ if (YY_@_CURRENT_BUFFER)
+ YY_@_DELETE_BUFFER(YY_@_CURRENT_BUFFER);
+}
+
+#endif
+
+
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+#ifndef YY_USER_INIT
+#define YY_USER_INIT
+#endif
+
+%% data tables for the DFA go here
+#ifndef YY_USE_CLASS
+static yy_state_type yy_get_previous_state YY_PROTO(( void ));
+static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state ));
+#else
+#define yy_get_previous_state() ((yy_state_type)(yy_get_previous_state_()))
+#define yy_try_NUL_trans(c) ((yy_state_type)(yy_try_NUL_trans_(c)))
+#endif
+
+#ifndef YY_USE_CLASS
+#ifdef YY_@_LEX_DEFINED
+YY_@_LEX_RETURN YY_@_LEX ( YY_@_LEX_PARAM )
+YY_@_LEX_PARAM_DEF
+#else
+YY_DECL
+#endif
+#else
+YY_@_LEX_RETURN YY_@_CLASS::YY_@_LEX ( YY_@_LEX_PARAM)
+
+#endif
+{
+ register yy_state_type yy_current_state;
+ register YY_@_CHAR *yy_cp, *yy_bp;
+ register int yy_act;
+
+%% user's declarations go here
+
+ if ( yy_init )
+ {
+
+ {
+ YY_USER_INIT;
+ }
+ if ( ! yy_start )
+ yy_start = 1; /* first start state */
+
+ if ( ! yy___in )
+ yy___in = YY_@_IFILE_DEFAULT;
+
+ if ( ! yy___out )
+ yy___out = YY_@_OFILE_DEFAULT;
+
+ if ( YY_@_CURRENT_BUFFER )
+ YY_@_INIT_BUFFER( YY_@_CURRENT_BUFFER, yy___in );
+ else
+ YY_@_CURRENT_BUFFER = YY_@_CREATE_BUFFER( yy___in, YY_BUF_SIZE );
+
+ YY_@_LOAD_BUFFER_STATE();
+ yy_init=0;
+ }
+
+ while ( 1 ) /* loops until end-of-file is reached */
+ {
+%% yymore()-related code goes here
+ yy_cp = yy_c_buf_p;
+
+ /* support of yy___text */
+ *yy_cp = yy_hold_char;
+
+ /* yy_bp points to the position in yy_ch_buf of the start of the
+ * current run.
+ */
+ yy_bp = yy_cp;
+
+%% code to set up and find next match goes here
+
+yy_find_action:
+%% code to find the action number goes here
+
+ YY_DO_BEFORE_ACTION;
+ YY_USER_ACTION;
+
+do_action: /* this label is used only to access EOF actions */
+#if YY_@_DEBUG != 0
+ if ( yy___flex_debug )
+ {
+ if ( yy_act == 0 )
+#ifndef YY_@_IOSTREAM
+ fprintf( yy___stderr , "--scanner backtracking\n" );
+#else
+ yy___stderr <<"--scanner backtracking"<<endl;
+#endif
+ else if ( yy_act < YY_END_OF_BUFFER -1 )
+#ifndef YY_@_IOSTREAM
+ fprintf( yy___stderr ,
+ "--accepting rule at line %d (\"%s\")\n",
+ yy_rule_linenum[yy_act], yy___text );
+#else
+ yy___stderr <<"--accepting rule at line "
+ <<(int)yy_rule_linenum[yy_act]
+ <<" (\""<<(char *)yy___text<<"\")"<<endl;
+#endif
+ else if ( yy_act == YY_END_OF_BUFFER -1 )
+#ifndef YY_@_IOSTREAM
+ fprintf( yy___stderr , "--accepting default rule (\"%s\")\n", yy___text );
+#else
+ yy___stderr <<"--accepting default rule (\""<<(char *)yy___text<<"\")"<<endl;
+#endif
+ else if ( yy_act == YY_END_OF_BUFFER )
+#ifndef YY_@_IOSTREAM
+ fprintf( yy___stderr , "--(end of buffer or a NUL)\n" );
+#else
+ yy___stderr <<"--(end of buffer or a NUL)"<<endl;
+#endif
+ else
+#ifndef YY_@_IOSTREAM
+ fprintf( yy___stderr , "--EOF\n" );
+#else
+ yy___stderr <<"--EOF"<<endl;
+#endif
+ }
+#endif
+ switch ( yy_act )
+ {
+%% actions go here
+
+ case YY_END_OF_BUFFER:
+ {
+ /* amount of text matched not including the EOB char */
+ int yy_amount_of_matched_text = yy_cp - yy___text - 1;
+
+ /* undo the effects of YY_DO_BEFORE_ACTION */
+ *yy_cp = yy_hold_char;
+
+ /* note that here we test for yy_c_buf_p "<=" to the position
+ * of the first EOB in the buffer, since yy_c_buf_p will
+ * already have been incremented past the NUL character
+ * (since all states make transitions on EOB to the end-
+ * of-buffer state). Contrast this with the test in yyinput().
+ */
+ if ( yy_c_buf_p <= &YY_@_CURRENT_BUFFER->yy_ch_buf[yy_n_chars] )
+ /* this was really a NUL */
+ {
+ yy_state_type yy_next_state;
+
+ yy_c_buf_p = yy___text + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ /* okay, we're now positioned to make the
+ * NUL transition. We couldn't have
+ * yy_get_previous_state() go ahead and do it
+ * for us because it doesn't know how to deal
+ * with the possibility of jamming (and we
+ * don't want to build jamming into it because
+ * then it will run more slowly)
+ */
+
+ yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+ yy_bp = yy___text + YY_MORE_ADJ;
+
+ if ( yy_next_state )
+ {
+ /* consume the NUL */
+ yy_cp = ++yy_c_buf_p;
+ yy_current_state = yy_next_state;
+ goto yy_match;
+ }
+ else
+ {
+%% code to do backtracking for compressed tables and set up yy_cp goes here
+ goto yy_find_action;
+ }
+ }
+ else switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ yy_did_buffer_switch_on_eof = 0;
+
+ if ( yywrap() )
+ {
+ /* note: because we've taken care in
+ * yy_get_next_buffer() to have set up yy___text,
+ * we can now set up yy_c_buf_p so that if some
+ * total hoser (like flex itself) wants
+ * to call the scanner after we return the
+ * YY_NULL, it'll still work - another YY_NULL
+ * will get returned.
+ */
+ yy_c_buf_p = yy___text + YY_MORE_ADJ;
+
+ yy_act = YY_STATE_EOF((yy_start - 1) / 2);
+ goto do_action;
+ }
+ else
+ {
+ if ( ! yy_did_buffer_switch_on_eof )
+ YY_NEW_FILE;
+ }
+ }
+ break;
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yy___text + yy_amount_of_matched_text;
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yy___text + YY_MORE_ADJ;
+ goto yy_match;
+
+ case EOB_ACT_LAST_MATCH:
+ yy_c_buf_p = &YY_@_CURRENT_BUFFER->yy_ch_buf[yy_n_chars];
+
+ yy_current_state = yy_get_previous_state();
+
+ yy_cp = yy_c_buf_p;
+ yy_bp = yy___text + YY_MORE_ADJ;
+ goto yy_find_action;
+ }
+ break;
+ }
+
+ default:
+#if YY_@_DEBUG != 0
+#ifndef YY_@_IOSTREAM
+ fprintf(yy___stderr , "action # %d\n", yy_act );
+#else
+ yy___stderr <<"action # "<<(int)yy_act<<endl;
+#endif
+#endif
+ YY_FATAL_ERROR("fatal flex scanner internal error--no action found" );
+ }
+ }
+ yyterminate();/* avoid the no return value error message on MS-C7/dos */
+}
+
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * synopsis
+ * int yy_get_next_buffer();
+ *
+ * returns a code representing an action
+ * EOB_ACT_LAST_MATCH -
+ * EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ * EOB_ACT_END_OF_FILE - end of file
+ */
+#ifndef YY_USE_CLASS
+static int yy_get_next_buffer()
+#else
+int YY_@_CLASS::yy_get_next_buffer()
+#endif
+{
+ register YY_@_CHAR *dest = YY_@_CURRENT_BUFFER->yy_ch_buf;
+ register YY_@_CHAR *source = yy___text - 1; /* copy prev. char, too */
+ register int number_to_move, i;
+ int ret_val;
+
+ if ( yy_c_buf_p > &YY_@_CURRENT_BUFFER->yy_ch_buf[yy_n_chars + 1] )
+ YY_FATAL_ERROR("fatal flex scanner internal error--end of buffer missed" );
+
+ /* try to read more data */
+
+ /* first move last chars to start of buffer */
+ number_to_move = yy_c_buf_p - yy___text;
+
+ for ( i = 0; i < number_to_move; ++i )
+ *(dest++) = *(source++);
+
+ if ( YY_@_CURRENT_BUFFER->yy_eof_status != EOF_NOT_SEEN )
+ /* don't do the read, it's not guaranteed to return an EOF,
+ * just force an EOF
+ */
+ yy_n_chars = 0;
+
+ else
+ {
+ int num_to_read = YY_@_CURRENT_BUFFER->yy_buf_size - number_to_move - 1;
+
+ if ( num_to_read > YY_READ_BUF_SIZE )
+ num_to_read = YY_READ_BUF_SIZE;
+
+ else if ( num_to_read <= 0 )
+ YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" );
+
+ /* read in more data */
+ YY_INPUT( (&YY_@_CURRENT_BUFFER->yy_ch_buf[number_to_move]), yy_n_chars, num_to_read );
+ }
+
+ if ( yy_n_chars == 0 )
+ {
+ if ( number_to_move - YY_MORE_ADJ == 1 )
+ {
+ ret_val = EOB_ACT_END_OF_FILE;
+ YY_@_CURRENT_BUFFER->yy_eof_status = EOF_DONE;
+ }
+ else
+ {
+ ret_val = EOB_ACT_LAST_MATCH;
+ YY_@_CURRENT_BUFFER->yy_eof_status = EOF_PENDING;
+ }
+ }
+ else
+ ret_val = EOB_ACT_CONTINUE_SCAN;
+
+ yy_n_chars += number_to_move;
+ YY_@_CURRENT_BUFFER->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR;
+ YY_@_CURRENT_BUFFER->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR;
+
+ /* yy___text begins at the second character in yy_ch_buf; the first
+ * character is the one which preceded it before reading in the latest
+ * buffer; it needs to be kept around in case it's a newline, so
+ * yy_get_previous_state() will have with '^' rules active
+ */
+
+ yy___text = &YY_@_CURRENT_BUFFER->yy_ch_buf[1];
+
+ return ( ret_val );
+}
+
+
+/* yy_get_previous_state - get the state just before the EOB char was reached
+ *
+ * synopsis
+ * yy_state_type yy_get_previous_state();
+ */
+
+#ifndef YY_USE_CLASS
+static yy_state_type yy_get_previous_state()
+#else
+long YY_@_CLASS::yy_get_previous_state_()
+#endif
+{
+ register yy_state_type yy_current_state;
+ register YY_@_CHAR *yy_cp;
+
+%% code to get the start state into yy_current_state goes here
+
+ for ( yy_cp = yy___text + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp )
+ {
+%% code to find the next state goes here
+ }
+
+#ifndef YY_USE_CLASS
+ return ( yy_current_state );
+#else
+ return (long)( yy_current_state );
+#endif
+}
+
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ * next_state = yy_try_NUL_trans( current_state );
+ */
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state )
+#else
+static yy_state_type yy_try_NUL_trans( yy_current_state )
+register yy_state_type yy_current_state;
+#endif
+#else
+long YY_@_CLASS::yy_try_NUL_trans_(long yy_current_state_)
+#endif
+
+{
+#ifndef YY_USE_CLASS
+#else
+ yy_state_type yy_current_state=(yy_state_type)yy_current_state_;
+#endif
+ register int yy_is_jam;
+%% code to find the next state, and perhaps do backtracking, goes here
+
+#ifndef YY_USE_CLASS
+ return ( yy_is_jam ? 0 : yy_current_state );
+#else
+ return (long)( yy_is_jam ? 0 : yy_current_state );
+#endif
+}
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+static void yyunput( YY_@_CHAR c, register YY_@_CHAR *yy_bp )
+#else
+static void yyunput( c, yy_bp )
+YY_@_CHAR c;
+register YY_@_CHAR *yy_bp;
+#endif
+#else
+void YY_@_CLASS::yyunput( YY_@_CHAR c, YY_@_CHAR *yy_bp )
+#endif
+
+{
+ register YY_@_CHAR *yy_cp = yy_c_buf_p;
+
+ /* undo effects of setting up yy___text */
+ *yy_cp = yy_hold_char;
+
+ if ( yy_cp < YY_@_CURRENT_BUFFER->yy_ch_buf + 2 )
+ { /* need to shift things up to make room */
+ register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */
+ register YY_@_CHAR *dest = &YY_@_CURRENT_BUFFER->yy_ch_buf[YY_@_CURRENT_BUFFER->yy_buf_size + 2];
+ register YY_@_CHAR *source = &YY_@_CURRENT_BUFFER->yy_ch_buf[number_to_move];
+
+ while ( source > YY_@_CURRENT_BUFFER->yy_ch_buf )
+ *--dest = *--source;
+
+ yy_cp += dest - source;
+ yy_bp += dest - source;
+ yy_n_chars = YY_@_CURRENT_BUFFER->yy_buf_size;
+
+ if ( yy_cp < YY_@_CURRENT_BUFFER->yy_ch_buf + 2 )
+ YY_FATAL_ERROR( "flex scanner push-back overflow" );
+ }
+
+ if ( yy_cp > yy_bp && yy_cp[-1] == '\n' )
+ yy_cp[-2] = '\n';
+
+ *--yy_cp = c;
+
+ /* note: the formal parameter *must* be called "yy_bp" for this
+ * macro to now work correctly
+ */
+ YY_DO_BEFORE_ACTION; /* set up yy___text again */
+}
+
+#ifndef YY_USE_CLASS
+#ifdef __cplusplus
+static int yyinput()
+#else
+static int input()
+#endif
+#else
+int YY_@_CLASS::input()
+#endif
+{
+ int c;
+ YY_@_CHAR *yy_cp = yy_c_buf_p;
+
+ *yy_cp = yy_hold_char;
+
+ if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR )
+ {
+ /* yy_c_buf_p now points to the character we want to return.
+ * If this occurs *before* the EOB characters, then it's a
+ * valid NUL; if not, then we've hit the end of the buffer.
+ */
+ if ( yy_c_buf_p < &YY_@_CURRENT_BUFFER->yy_ch_buf[yy_n_chars] )
+ /* this was really a NUL */
+ *yy_c_buf_p = '\0';
+ else
+ { /* need more input */
+ yy___text = yy_c_buf_p;
+ ++yy_c_buf_p;
+
+ switch ( yy_get_next_buffer() )
+ {
+ case EOB_ACT_END_OF_FILE:
+ {
+ if ( yywrap() )
+ {
+ yy_c_buf_p = yy___text + YY_MORE_ADJ;
+ return ( EOF );
+ }
+
+ YY_NEW_FILE;
+#ifndef YY_USE_CLASS
+#ifdef __cplusplus
+ return ( yyinput() );
+#else
+ return ( input() );
+#endif
+#else
+ return ( input() );
+#endif
+ }
+ break;
+
+ case EOB_ACT_CONTINUE_SCAN:
+ yy_c_buf_p = yy___text + YY_MORE_ADJ;
+ break;
+
+ case EOB_ACT_LAST_MATCH:
+#ifndef YY_USE_CLASS
+#ifdef __cplusplus
+ YY_FATAL_ERROR( "unexpected last match in yyinput()" );
+#else
+ YY_FATAL_ERROR( "unexpected last match in input()" );
+#endif
+#else
+ YY_FATAL_ERROR( "unexpected last match in YY_@_CLASS::input()" );
+#endif
+ }
+ }
+ }
+
+ c = *yy_c_buf_p;
+ yy_hold_char = *++yy_c_buf_p;
+
+ return ( c );
+}
+
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+void YY_@_RESTART( YY_@_IFILE *input_file )
+#else
+void YY_@_RESTART( input_file )
+YY_@_IFILE *input_file;
+#endif
+#else
+void YY_@_CLASS::YY_@_RESTART ( YY_@_IFILE *input_file )
+#endif
+
+{
+ YY_@_INIT_BUFFER( YY_@_CURRENT_BUFFER, input_file );
+ YY_@_LOAD_BUFFER_STATE();
+}
+
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+void YY_@_SWITCH_TO_BUFFER( YY_BUFFER_STATE new_buffer )
+#else
+void YY_@_SWITCH_TO_BUFFER( new_buffer )
+YY_BUFFER_STATE new_buffer;
+#endif
+#else
+void YY_@_CLASS::YY_@_SWITCH_TO_BUFFER( YY_BUFFER_STATE new_buffer )
+#endif
+
+{
+ if ( YY_@_CURRENT_BUFFER == new_buffer )
+ return;
+
+ if ( YY_@_CURRENT_BUFFER )
+ {
+ /* flush out information for old buffer */
+ *yy_c_buf_p = yy_hold_char;
+ YY_@_CURRENT_BUFFER->yy_buf_pos = yy_c_buf_p;
+ YY_@_CURRENT_BUFFER->yy_n_chars = yy_n_chars;
+ }
+
+ YY_@_CURRENT_BUFFER = new_buffer;
+ YY_@_LOAD_BUFFER_STATE();
+
+ /* we don't actually know whether we did this switch during
+ * EOF (yywrap()) processing, but the only time this flag
+ * is looked at is after yywrap() is called, so it's safe
+ * to go ahead and always set it.
+ */
+ yy_did_buffer_switch_on_eof = 1;
+}
+
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+void YY_@_LOAD_BUFFER_STATE( void )
+#else
+void YY_@_LOAD_BUFFER_STATE()
+#endif
+#else
+void YY_@_CLASS::YY_@_LOAD_BUFFER_STATE( )
+#endif
+
+{
+ yy_n_chars = YY_@_CURRENT_BUFFER->yy_n_chars;
+ yy___text = yy_c_buf_p = YY_@_CURRENT_BUFFER->yy_buf_pos;
+ yy___in = YY_@_CURRENT_BUFFER->yy_input_file;
+ yy_hold_char = *yy_c_buf_p;
+}
+
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+YY_BUFFER_STATE YY_@_CREATE_BUFFER( YY_@_IFILE *file, int size )
+#else
+YY_BUFFER_STATE YY_@_CREATE_BUFFER( file, size )
+YY_@_IFILE *file;
+int size;
+#endif
+#else
+YY_BUFFER_STATE YY_@_CLASS::YY_@_CREATE_BUFFER( YY_@_IFILE *file, int size )
+#endif
+
+{
+ YY_BUFFER_STATE b;
+
+ b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) );
+
+ if ( ! b )
+ YY_FATAL_ERROR( "out of dynamic memory in YY_@_CREATE_BUFFER()" );
+
+ b->yy_buf_size = size;
+
+ /* yy_ch_buf has to be 2 characters longer than the size given because
+ * we need to put in 2 end-of-buffer characters.
+ */
+ b->yy_ch_buf = (YY_@_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) );
+
+ if ( ! b->yy_ch_buf )
+ YY_FATAL_ERROR( "out of dynamic memory in YY_@_CREATE_BUFFER()" );
+
+ YY_@_INIT_BUFFER( b, file );
+
+ return ( b );
+}
+
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+void YY_@_DELETE_BUFFER( YY_BUFFER_STATE b )
+#else
+void YY_@_DELETE_BUFFER( b )
+YY_BUFFER_STATE b;
+#endif
+#else
+void YY_@_CLASS::YY_@_DELETE_BUFFER( YY_BUFFER_STATE b )
+#endif
+
+{
+ if ( b == YY_@_CURRENT_BUFFER )
+ YY_@_CURRENT_BUFFER = (YY_BUFFER_STATE) 0;
+
+ free( (char *) b->yy_ch_buf );
+ free( (char *) b );
+}
+
+
+#ifndef YY_USE_CLASS
+#ifdef YY_USE_PROTOS
+void YY_@_INIT_BUFFER( YY_BUFFER_STATE b, YY_@_IFILE *file )
+#else
+void YY_@_INIT_BUFFER( b, file )
+YY_BUFFER_STATE b;
+YY_@_IFILE *file;
+#endif
+#else
+void YY_@_CLASS::YY_@_INIT_BUFFER( YY_BUFFER_STATE b, YY_@_IFILE *file)
+#endif
+
+{
+ b->yy_input_file = file;
+
+ /* we put in the '\n' and start reading from [1] so that an
+ * initial match-at-newline will be true.
+ */
+
+ b->yy_ch_buf[0] = '\n';
+ b->yy_n_chars = 1;
+
+ /* we always need two end-of-buffer characters. The first causes
+ * a transition to the end-of-buffer state. The second causes
+ * a jam in that state.
+ */
+ b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+ b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR;
+
+ b->yy_buf_pos = &b->yy_ch_buf[1];
+
+ b->yy_eof_status = EOF_NOT_SEEN;
+}
+
diff --git a/tools/mhmake/src/flexskel.h b/tools/mhmake/src/flexskel.h
new file mode 100644
index 000000000..8e02a7b32
--- /dev/null
+++ b/tools/mhmake/src/flexskel.h
@@ -0,0 +1,395 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* A lexical scanner header generated by flex */
+/* MODIFIED FOR C++ CLASS BY Alain Coetmeur: coetmeur(at)icdc.fr */
+/* Note that (at) mean the 'at' symbol that I cannot write */
+/* because it is expanded to the class name */
+/* made at Informatique-CDC, Research&development department */
+/* company from the Caisse Des Depots et Consignations */
+
+
+/*********************************************/
+/* SYSTEM dependent declaration, includes... */
+/*********************************************/
+/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */
+#ifdef c_plusplus
+#ifndef __cplusplus
+#define __cplusplus
+#endif
+#endif
+#ifdef __cplusplus
+#ifndef YY_USE_PROTOS
+#define YY_USE_PROTOS
+#endif
+#ifndef YY_USE_CLASS
+#define YY_USE_CLASS
+#endif
+#else /* ! __cplusplus */
+#ifdef __STDC__
+#ifdef __GNUC__
+#else
+#endif /* __GNUC__ */
+#ifndef YY_USE_PROTOS
+#define YY_USE_PROTOS
+#endif
+#endif /* __STDC__ */
+#endif /* ! __cplusplus */
+/*********************************************/
+/* COMPILER DEPENDENT MACROS */
+/*********************************************/
+/* use prototypes in function declarations */
+#ifndef YY_PROTO
+#ifdef YY_USE_PROTOS
+#define YY_PROTO(proto) proto
+#else
+#define YY_PROTO(proto) ()
+#endif
+#endif
+#include <stdio.h>
+
+
+
+
+%% here is the declaration from section1 %header{
+
+#ifdef YY_USE_CLASS
+#ifdef YY_@_IOSTREAM
+#include <iostream.h>
+#define YY_@_IFILE istream
+#define YY_@_OFILE ostream
+#define YY_@_ERRFILE cerr
+
+#ifndef YY_@_IFILE_DEFAULT
+#define YY_@_IFILE_DEFAULT &cin
+#endif
+
+#ifndef YY_@_OFILE_DEFAULT
+#define YY_@_OFILE_DEFAULT &cout
+#endif
+
+#endif
+#endif
+
+#ifndef YY_@_IFILE
+#define YY_@_IFILE FILE
+#endif
+
+#ifndef YY_@_OFILE
+#define YY_@_OFILE FILE
+#endif
+
+#ifndef YY_@_ERRFILE
+#define YY_@_ERRFILE stderr
+#endif
+
+#ifndef YY_@_IFILE_DEFAULT
+#define YY_@_IFILE_DEFAULT stdin
+#endif
+
+#ifndef YY_@_OFILE_DEFAULT
+#define YY_@_OFILE_DEFAULT stdout
+#endif
+
+
+
+
+#ifndef YY_@_TEXT
+#define YY_@_TEXT yytext
+#endif
+#ifndef YY_@_LENG
+#define YY_@_LENG yyleng
+#endif
+#ifndef YY_@_IN
+#define YY_@_IN yyin
+#endif
+#ifndef YY_@_OUT
+#define YY_@_OUT yyout
+#endif
+
+#ifndef YY_@_LEX_RETURN
+#define YY_@_LEX_RETURN int
+#else
+#ifndef YY_@_LEX_DEFINED
+#define YY_@_LEX_DEFINED
+#endif
+#endif
+
+#ifndef YY_@_LEX
+#define YY_@_LEX yylex
+#else
+#ifndef YY_@_LEX_DEFINED
+#define YY_@_LEX_DEFINED
+#endif
+#endif
+
+#ifndef YY_@_LEX_PARAM
+#ifndef YY_USE_PROTOS
+#define YY_@_LEX_PARAM
+#else
+#define YY_@_LEX_PARAM void
+#endif
+#else
+#ifndef YY_@_LEX_DEFINED
+#define YY_@_LEX_DEFINED
+#endif
+#endif
+
+#ifndef YY_@_LEX_PARAM_DEF
+#define YY_@_LEX_PARAM_DEF
+#else
+#ifndef YY_@_LEX_DEFINED
+#define YY_@_LEX_DEFINED
+#endif
+#endif
+
+#ifndef YY_@_RESTART
+#define YY_@_RESTART yyrestart
+#endif
+#ifndef YY_@_SWITCH_TO_BUFFER
+#define YY_@_SWITCH_TO_BUFFER yy_switch_to_buffer
+#endif
+#ifndef YY_@_LOAD_BUFFER_STATE
+#define YY_@_LOAD_BUFFER_STATE yy_load_buffer_state
+#endif
+
+#ifndef YY_@_CREATE_BUFFER
+#define YY_@_CREATE_BUFFER yy_create_buffer
+#ifndef YY_USE_CLASS
+#ifndef yy_new_buffer
+#define yy_new_buffer yy_create_buffer
+#endif
+#endif
+#endif
+#ifndef YY_@_DELETE_BUFFER
+#define YY_@_DELETE_BUFFER yy_delete_buffer
+#endif
+#ifndef YY_@_INIT_BUFFER
+#define YY_@_INIT_BUFFER yy_init_buffer
+#endif
+
+
+
+#ifdef YY_@_FLEX_DEBUG
+#ifndef YY_@_DEBUG
+#define YY_@_DEBUG 1
+#endif
+#else
+#ifndef YY_@_DEBUG
+#define YY_@_DEBUG 0
+#endif
+#endif
+
+#if YY_@_DEBUG != 0
+#ifndef YY_@_DEBUG_FLAG
+#define YY_@_DEBUG_FLAG yy_flex_debug
+#endif
+#ifndef YY_@_DEBUG_INIT
+#define YY_@_DEBUG_INIT 1
+#endif
+#endif
+
+
+
+
+#ifndef YY_USE_CLASS
+#ifndef YY_@_CURRENT_BUFFER
+#define YY_@_CURRENT_BUFFER yy_current_buffer
+#endif
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+extern void YY_@_RESTART YY_PROTO(( YY_@_IFILE *input_file ));
+extern void YY_@_SWITCH_TO_BUFFER YY_PROTO(( YY_BUFFER_STATE new_buffer ));
+extern void YY_@_LOAD_BUFFER_STATE YY_PROTO(( void ));
+extern YY_BUFFER_STATE YY_@_CREATE_BUFFER YY_PROTO(( YY_@_IFILE *file, int size ));
+extern void YY_@_DELETE_BUFFER YY_PROTO(( YY_BUFFER_STATE b ));
+extern void YY_@_INIT_BUFFER YY_PROTO(( YY_BUFFER_STATE b, YY_@_IFILE *file ));
+
+#if YY_@_DEBUG != 0
+extern int YY_@_DEBUG_FLAG ;
+#endif
+extern YY_@_CHAR *YY_@_TEXT;
+extern int YY_@_LENG;
+extern YY_@_IFILE *YY_@_IN;
+extern YY_@_OFILE *YY_@_OUT;
+#ifdef YY_@_LEX_DEFINED
+extern YY_@_LEX_RETURN YY_@_LEX ( YY_@_LEX_PARAM )
+YY_@_LEX_PARAM_DEF
+#else
+#ifndef YY_DECL
+extern YY_@_LEX_RETURN YY_@_LEX ( YY_@_LEX_PARAM )
+YY_@_LEX_PARAM_DEF
+#else
+/* no declaration if oldstyle flex */
+#endif
+#endif
+#else
+
+#ifndef YY_@_CURRENT_BUFFER
+#define YY_@_CURRENT_BUFFER YY_CURRENT_BUFFER
+#endif
+#ifndef YY_@_CLASS
+#define YY_@_CLASS @
+#endif
+#ifndef YY_@_ECHO
+#define YY_@_ECHO yy_echo
+#endif
+#ifdef YY_@_ECHO_PURE
+#define YY_@_ECHO_NOCODE
+#endif
+
+#ifndef YY_@_ECHO_CODE
+#ifndef YY_@_IOSTREAM
+#define YY_@_ECHO_CODE fwrite( (char *) YY_@_TEXT, YY_@_LENG, 1, YY_@_OUT );
+#else
+#define YY_@_ECHO_CODE (YY_@_OUT->write( (char *) YY_@_TEXT, YY_@_LENG));
+#endif
+#endif
+
+#ifndef YY_@_INPUT
+#define YY_@_INPUT yy_input
+#endif
+#ifdef YY_@_INPUT_PURE
+#define YY_@_INPUT_NOCODE
+#endif
+
+#ifndef YY_@_INPUT_CODE
+#ifndef YY_@_IOSTREAM
+#define YY_@_INPUT_CODE return result= ::fread( buffer, 1,max_size,YY_@_IN );
+#else
+#define YY_@_INPUT_CODE if(YY_@_IN->eof()) result=0;else {YY_@_IN->read(buffer,max_size);result=YY_@_IN->gcount();YY_@_IN->clear(YY_@_IN->rdstate()&(~ios::failbit));if(YY_@_IN->bad()) result= -1;} return result;
+#endif
+#endif
+
+#ifdef YY_@_FATAL_ERROR_PURE
+#define YY_@_FATAL_ERRO_NOCODE
+#endif
+#ifndef YY_@_FATAL_ERROR
+#define YY_@_FATAL_ERROR yy_fatal_error
+#endif
+
+#ifndef YY_@_FATAL_ERROR_CODE
+#ifndef YY_@_IOSTREAM
+#define YY_@_FATAL_ERROR_CODE fputs( msg, YY_@_ERRFILE );putc( '\n', YY_@_ERRFILE );exit( 1 );
+#else
+#define YY_@_FATAL_ERROR_CODE YY_@_ERRFILE<< msg <<endl;exit( 1 );
+#endif
+#endif
+
+#ifndef YY_@_WRAP
+#define YY_@_WRAP yy_wrap
+#endif
+#ifdef YY_@_WRAP_PURE
+#define YY_@_WRAP_NOCODE
+#endif
+#ifndef YY_@_WRAP_CODE
+#define YY_@_WRAP_CODE return 1;
+#endif
+
+
+#ifndef YY_@_INHERIT
+#define YY_@_INHERIT
+#endif
+#ifndef YY_@_MEMBERS
+#define YY_@_MEMBERS
+#endif
+#ifndef YY_@_CONSTRUCTOR_PARAM
+#define YY_@_CONSTRUCTOR_PARAM
+#endif
+#ifndef YY_@_CONSTRUCTOR_CODE
+#define YY_@_CONSTRUCTOR_CODE
+#endif
+#ifndef YY_@_CONSTRUCTOR_INIT
+#define YY_@_CONSTRUCTOR_INIT
+#endif
+#ifndef YY_@_DESTRUCTOR_CODE
+#define YY_@_DESTRUCTOR_CODE
+#endif
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+
+class YY_@_CLASS YY_@_INHERIT
+{
+private:/* data */
+ YY_@_CHAR *yy_c_buf_p;
+ YY_@_CHAR yy_hold_char;
+ int yy_n_chars;
+ int yy_init;
+ int yy_start;
+ int yy_did_buffer_switch_on_eof;
+private: /* functions */
+ void yy_initialize();
+ int input();
+ int yyinput() {return input();};
+ int yy_get_next_buffer();
+ void yyunput( YY_@_CHAR c, YY_@_CHAR *buf_ptr );
+ /* use long instead of yy_state_type because it is undef */
+ long yy_get_previous_state_ ( void );
+ long yy_try_NUL_trans_ ( long current_state_ );
+protected:/* non virtual */
+ YY_BUFFER_STATE YY_@_CURRENT_BUFFER;
+ void YY_@_RESTART ( YY_@_IFILE *input_file );
+ void YY_@_SWITCH_TO_BUFFER( YY_BUFFER_STATE new_buffer );
+ void YY_@_LOAD_BUFFER_STATE( void );
+ YY_BUFFER_STATE YY_@_CREATE_BUFFER( YY_@_IFILE *file, int size );
+ void YY_@_DELETE_BUFFER( YY_BUFFER_STATE b );
+ void YY_@_INIT_BUFFER( YY_BUFFER_STATE b, YY_@_IFILE *file );
+ protected: /* virtual */
+ virtual void YY_@_ECHO()
+#ifdef YY_@_ECHO_PURE
+ =0
+#endif
+ ;
+ virtual int YY_@_INPUT(char *buf,int &result,int max_size)
+#ifdef YY_@_INPUT_PURE
+ =0
+#endif
+ ;
+ virtual void YY_@_FATAL_ERROR(char *msg)
+#ifdef YY_@_FATAL_ERROR_PURE
+ =0
+#endif
+ ;
+ virtual int YY_@_WRAP()
+#ifdef YY_@_WRAP_PURE
+ =0
+#endif
+ ;
+public:
+ YY_@_CHAR *YY_@_TEXT;
+ int YY_@_LENG;
+ YY_@_IFILE *YY_@_IN;
+ YY_@_OFILE *YY_@_OUT;
+ YY_@_LEX_RETURN YY_@_LEX ( YY_@_LEX_PARAM);
+ YY_@_CLASS(YY_@_CONSTRUCTOR_PARAM) ;
+ virtual ~YY_@_CLASS() ;
+#if YY_@_DEBUG != 0
+ int YY_@_DEBUG_FLAG;
+#endif
+public: /* added members */
+ YY_@_MEMBERS
+};
+#endif
+
+/* declaration of externs for public use of yylex scanner */
+
+%% here is the declaration from section2 %header{
+
+/* end of generated header */
+
diff --git a/tools/mhmake/src/functions.cpp b/tools/mhmake/src/functions.cpp
new file mode 100644
index 000000000..4ae9ddd2e
--- /dev/null
+++ b/tools/mhmake/src/functions.cpp
@@ -0,0 +1,767 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "util.h"
+#include "mhmakefileparser.h"
+#include "rule.h"
+
+static const string s_QuoteString("\"");
+
+funcdef mhmakefileparser::m_FunctionsDef[]= {
+ {"filter", &mhmakefileparser::f_filter}
+ ,{"call", &mhmakefileparser::f_call}
+ ,{"subst", &mhmakefileparser::f_subst}
+ ,{"patsubst", &mhmakefileparser::f_patsubst}
+ ,{"concat", &mhmakefileparser::f_concat}
+ ,{"if", &mhmakefileparser::f_if}
+ ,{"findstring", &mhmakefileparser::f_findstring}
+ ,{"firstword", &mhmakefileparser::f_firstword}
+ ,{"wildcard", &mhmakefileparser::f_wildcard}
+ ,{"basename", &mhmakefileparser::f_basename}
+ ,{"notdir", &mhmakefileparser::f_notdir}
+ ,{"dir", &mhmakefileparser::f_dir}
+ ,{"shell", &mhmakefileparser::f_shell}
+ ,{"relpath", &mhmakefileparser::f_relpath}
+ ,{"toupper", &mhmakefileparser::f_toupper}
+ ,{"tolower", &mhmakefileparser::f_tolower}
+ ,{"exist", &mhmakefileparser::f_exist}
+ ,{"filesindirs",&mhmakefileparser::f_filesindirs}
+ ,{"fullname" ,&mhmakefileparser::f_fullname}
+ ,{"addprefix" ,&mhmakefileparser::f_addprefix}
+ ,{"addsuffix" ,&mhmakefileparser::f_addsuffix}
+ ,{"filter-out" ,&mhmakefileparser::f_filterout}
+ ,{"word" ,&mhmakefileparser::f_word}
+ ,{"words" ,&mhmakefileparser::f_words}
+};
+
+map<string,function_f> mhmakefileparser::m_Functions;
+
+bool mhmakefileparser::m_FunctionsInitialised;
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::InitFuncs(void)
+{
+ for (int i=0; i<sizeof(m_FunctionsDef)/sizeof(funcdef); i++)
+ m_Functions[m_FunctionsDef[i].szFuncName]=m_FunctionsDef[i].pFunc;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string TrimString(const string &Input)
+{
+ unsigned Start=0;
+ while (strchr(" \t",Input[Start])) Start++;
+ if (Start>=Input.size())
+ return g_EmptyString;
+ unsigned Stop=Input.size()-1;
+ while (strchr(" \t",Input[Stop])) Stop--;
+ return Input.substr(Start,Stop-Start+1);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_filter(const string & Arg) const
+{
+ int ipos=Arg.find(',');
+ #ifdef _DEBUG
+ if (ipos==string::npos) {
+ cerr << "filter func should have 2 arguments: "<<Arg<<endl;
+ throw 1;
+ }
+ #endif
+ string Str=TrimString(Arg.substr(0,ipos));
+ string List=Arg.substr(ipos+1);
+
+ if (Str.empty())
+ return Str;
+
+ bool First=true;
+ string Ret;
+ char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
+ while (pTok)
+ {
+ string Item(pTok);
+ if (PercentMatchList(Item,Str))
+ {
+ if (First)
+ {
+ Ret=Item;
+ First=false;
+ }
+ else
+ {
+ Ret+=g_SpaceString;
+ Ret+=Item;
+ }
+ }
+ pTok=strtok(NULL," \t");
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_filterout(const string & Arg) const
+{
+ int ipos=Arg.find(',');
+ #ifdef _DEBUG
+ if (ipos==string::npos) {
+ cerr << "filter func should have 2 arguments: "<<Arg<<endl;
+ throw 1;
+ }
+ #endif
+ string Str=TrimString(Arg.substr(0,ipos));
+ string List=Arg.substr(ipos+1);
+
+ if (Str.empty())
+ return Str;
+
+ bool First=true;
+ string Ret;
+ char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
+ while (pTok)
+ {
+ string Item(pTok);
+ if (!PercentMatchList(Item,Str))
+ {
+ if (First)
+ {
+ Ret=Item;
+ First=false;
+ }
+ else
+ {
+ Ret+=g_SpaceString;
+ Ret+=Item;
+ }
+ }
+ pTok=strtok(NULL," \t");
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_call(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ bool LastCharIsComma=Arg[Arg.length()-1]==',';
+
+ string Func;
+ pTmp=NextCharItem(pTmp,Func,',');
+ map<string,string>::const_iterator pFunc=m_Variables.find(Func);
+ #ifdef _DEBUG
+ if (pFunc==m_Variables.end())
+ {
+ fprintf(stderr,"call to non existing function %s\n",Func.c_str());
+ throw(1);
+ }
+ #endif
+ Func=pFunc->second;
+ int i=0;
+ while (*pTmp || LastCharIsComma) {
+ if (!*pTmp)
+ LastCharIsComma=false; /* To stop the loop */
+ string Repl;
+ pTmp=NextCharItem(pTmp,Repl,',');
+ i++;
+ char Tmp[10];
+ ::sprintf(Tmp,"$(%d)",i);
+ int Len=::strlen(Tmp);
+ int Pos=Func.find(Tmp);
+ while (Func.npos!=Pos)
+ {
+ Func=Func.substr(0,Pos)+Repl+Func.substr(Pos+Len);
+ Pos=Func.find(Tmp,Pos+Len);
+ }
+ }
+
+ return ExpandExpression(Func);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_subst(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string FromString;
+ pTmp=NextCharItem(pTmp,FromString,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ cerr << "Wrong number of arguments in function subst" << endl;
+ throw(1);
+ }
+ #endif
+
+ string ToString;
+ pTmp=NextCharItem(pTmp,ToString,',');
+ string Text;
+ NextCharItem(pTmp,Text,',');
+
+ if (FromString.empty())
+ return Text;
+
+ string Ret;
+ int Pos=Text.find(FromString);
+ int PrevPos=0;
+ while (Pos!=string::npos)
+ {
+ Ret+=Text.substr(PrevPos,Pos-PrevPos);
+ Ret+=ToString;
+ PrevPos=Pos+FromString.length();
+ Pos=Text.find(FromString,PrevPos);
+ }
+ Ret+=Text.substr(PrevPos);
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_patsubst(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string FromString;
+ pTmp=NextCharItem(pTmp,FromString,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ cerr << "Wrong number of arguments in function subst" << endl;
+ throw(1);
+ }
+ #endif
+
+ string ToString;
+ pTmp=NextCharItem(pTmp,ToString,',');
+ string Text;
+ NextCharItem(pTmp,Text,',');
+
+ if (FromString.empty())
+ return Text;
+
+ return Substitute(Text,FromString,ToString);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_concat(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string JoinString;
+ pTmp=NextCharItem(pTmp,JoinString,',');
+
+ string List;
+ pTmp=NextCharItem(pTmp,List,',');
+
+ if (JoinString.empty() && List.empty())
+ {
+ /* assume as $(concat ,,items) construct */
+ JoinString=",";
+ pTmp=NextCharItem(pTmp,List,',');
+ }
+
+ bool First=true;
+ string Ret;
+ char *pTok=strtok((char*)List.c_str()," \t"); // doing this is changing string, so this is very dangerous
+ while (pTok)
+ {
+ if (First)
+ {
+ First=false;
+ }
+ else
+ {
+ Ret+=JoinString;
+ }
+ Ret+=pTok;
+ pTok=strtok(NULL," \t");
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_if(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string Cond;
+ pTmp=NextCharItem(pTmp,Cond,',');
+ string Ret;
+ if (Cond.empty())
+ {
+ pTmp=NextCharItem(pTmp,Ret,',');
+ }
+ NextCharItem(pTmp,Ret,',');
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_findstring(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+ string find;
+ pTmp=NextCharItem(pTmp,find,',');
+ string instr;
+ NextCharItem(pTmp,instr,',');
+
+ if (instr.find(find) != instr.npos)
+ return find;
+ else
+ return g_EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_firstword(const string & Arg) const
+{
+ string FirstWord;
+ NextItem(Arg.c_str(),FirstWord);
+ return FirstWord;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_wildcard(const string & Arg) const
+{
+ string FileSpec=TrimString(Arg);
+ int LastSep=FileSpec.find_last_of(OSPATHSEP)+1;
+ string Dir=FileSpec.substr(0,LastSep);
+#ifdef WIN32
+ struct _finddata_t FileInfo;
+ long hFile=_findfirst(FileSpec.c_str(),&FileInfo);
+ if (hFile==-1)
+ return g_EmptyString;
+
+ string Ret=g_EmptyString;
+
+ /* We have to verify with percentmatch since the find functions *.ext also matches the functions *.xmlbrol */
+ string CheckSpec=FileSpec.substr(LastSep);
+ if (PercentMatch(FileInfo.name,CheckSpec,NULL,'*'))
+ {
+ Ret=Dir+FileInfo.name;
+ }
+ while (-1!=_findnext(hFile,&FileInfo))
+ {
+ if (PercentMatch(FileInfo.name,CheckSpec,NULL,'*'))
+ {
+ Ret+=g_SpaceString;
+ Ret+=Dir;
+ Ret+=FileInfo.name;
+ }
+ }
+ _findclose(hFile);
+#else
+ glob_t Res;
+ if (glob (FileSpec.c_str(), GLOB_ERR|GLOB_NOSORT|GLOB_MARK, NULL, &Res))
+ return g_EmptyString;
+
+ string Ret=g_EmptyString;
+ string SepStr=g_EmptyString;
+ for (int i=0; i<Res.gl_pathc; i++)
+ {
+ if (PercentMatch(Res.gl_pathv[i],FileSpec,NULL,'*'))
+ {
+ Ret+=SepStr;
+ Ret+=Res.gl_pathv[i];
+ SepStr=g_SpaceString;
+ }
+ }
+
+ globfree(&Res);
+#endif
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_exist(const string & Arg) const
+{
+ string File=TrimString(Arg);
+ refptr<fileinfo> pFile=GetFileInfo(File);
+ if (pFile->Exists())
+ {
+ return string("1");
+ }
+ else
+ return g_EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_filesindirs(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string strFiles;
+ pTmp=NextCharItem(pTmp,strFiles,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ cerr << "Wrong number of arguments in function filesindirs" << endl;
+ throw(1);
+ }
+ #endif
+ string strDirs;
+ NextCharItem(pTmp,strDirs,',');
+
+ vector< refptr<fileinfo> > Dirs;
+ SplitToItems(strDirs,Dirs);
+
+ pTmp=strFiles.c_str();
+ string Ret=g_EmptyString;
+ bool first=true;
+ while (*pTmp)
+ {
+ string File;
+ refptr<fileinfo> pFile;
+ pTmp=NextItem(pTmp,File);
+
+ vector< refptr<fileinfo> >::iterator It=Dirs.begin();
+ vector< refptr<fileinfo> >::iterator ItEnd=Dirs.end();
+ while (It!=ItEnd)
+ {
+ pFile=GetFileInfo(File,*It++);
+ if (pFile->Exists())
+ {
+ break;
+ }
+ pFile=NullFileInfo;
+ }
+ if (!pFile)
+ continue;
+
+ if (!first)
+ {
+ Ret+=g_SpaceString;
+ }
+ else
+ {
+ first=false;
+ }
+ Ret+=pFile->GetFullFileName();
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_fullname(const string & Arg) const
+{
+ string File=TrimString(Arg);
+ refptr<fileinfo> pFile=GetFileInfo(File);
+ return pFile->GetFullFileName();
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string IterList(const string &List,string (*iterFunc)(const string &FileName,const string &Arg),const string &Arg=NullString)
+{
+ const char *pTmp=List.c_str();
+ string Ret=g_EmptyString;
+ bool first=true;
+ while (*pTmp)
+ {
+ if (!first)
+ {
+ Ret+=g_SpaceString;
+ }
+ else
+ {
+ first=false;
+ }
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ Ret+=iterFunc(Item,Arg);
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string basename(const string &FileName,const string &)
+{
+ string Ret=FileName.substr(0,FileName.find_last_of('.'));
+ if (FileName[0]=='"')
+ Ret+=s_QuoteString;
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_basename(const string & FileNames) const
+{
+ return IterList(FileNames,basename);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string notdir(const string &FileName,const string &)
+{
+ int Pos=FileName.find_last_of(OSPATHSEP);
+ if (Pos==string::npos)
+ {
+ return FileName;
+ }
+ else
+ {
+ string Ret=g_EmptyString;
+ if (FileName[0]=='"')
+ Ret+=s_QuoteString;
+ Ret+=FileName.substr(Pos+1);
+ return Ret;
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_notdir(const string & FileNames) const
+{
+ return IterList(FileNames,notdir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string addprefix(const string &FileName,const string &Prefix)
+{
+ return Prefix+FileName;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_addprefix(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+ string PreFix;
+ pTmp=NextCharItem(pTmp,PreFix,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ cerr << "Wrong number of arguments in function addprefix" << endl;
+ throw(1);
+ }
+ #endif
+ string FileNames;
+ pTmp=NextCharItem(pTmp,FileNames,',');
+ return IterList(FileNames,addprefix,PreFix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string addsuffix(const string &FileName,const string &Suffix)
+{
+ return FileName+Suffix;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_addsuffix(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+ string SufFix;
+ pTmp=NextCharItem(pTmp,SufFix,',');
+ #ifdef _DEBUG
+ if (!*pTmp) {
+ cerr << "Wrong number of arguments in function addsuffix" << endl;
+ throw(1);
+ }
+ #endif
+ string FileNames;
+ pTmp=NextCharItem(pTmp,FileNames,',');
+ return IterList(FileNames,addsuffix,SufFix);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns the n-th word number
+string mhmakefileparser::f_word(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+
+ string strNum;
+ pTmp=NextCharItem(pTmp,strNum,',');
+
+ int WordNbr=atoi(strNum.c_str());
+
+ #ifdef _DEBUG
+ if (!WordNbr)
+ {
+ if (!WordNbr) {
+ cerr << "Expecting a number bigger then 0 for the word function"<<endl;
+ throw(1);
+ }
+ }
+ #endif
+
+ int CurWord=0;
+ while (*pTmp)
+ {
+ string Word;
+ pTmp=NextItem(pTmp,Word);
+ CurWord++;
+ if (CurWord==WordNbr)
+ return Word;
+ }
+
+ return g_EmptyString;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns the number of words
+string mhmakefileparser::f_words(const string & Arg) const
+{
+ const char *pTmp=Arg.c_str();
+ int NrWords=0;
+ char szNumber[10];
+ while (*pTmp)
+ {
+ string Word;
+ pTmp=NextItem(pTmp,Word);
+ NrWords++;
+ }
+ sprintf(szNumber,"%d",NrWords);
+
+ return szNumber;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string dir(const string &FileName,const string &)
+{
+ int Pos=FileName.find_last_of(OSPATHSEP);
+ if (Pos==string::npos)
+ {
+ return g_EmptyString;
+ }
+ else
+ {
+ string Ret=g_EmptyString;
+ Ret+=FileName.substr(0,Pos+1);
+ if (FileName[0]=='"')
+ Ret+=s_QuoteString;
+ return Ret;
+ }
+
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_dir(const string & FileNames) const
+{
+ return IterList(FileNames,dir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_shell(const string & Command) const
+{
+ string Output;
+
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "shell: executing: Command '"<<Command<<"'"<<endl;
+#endif
+
+ ((mhmakefileparser*)this)->ExecuteCommand(string("@")+Command,&Output); // Make sure that the command is not echoed
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "shell returned '"<<Output<<"'"<<endl;
+#endif
+ return Output;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string relpath(const string &FileName,const string &)
+{
+ refptr<fileinfo> Path=GetFileInfo(FileName);
+ const char *pCur=curdir::GetCurDir()->GetFullFileName().c_str();
+ const char *pPath=Path->GetFullFileName().c_str();
+
+ const char *pLast=pPath;
+ while (*pCur==*pPath)
+ {
+ char Char=*pPath;
+ if (!Char)
+ {
+ return "."; // Means that FileName is the same as the current directory
+ }
+ if (Char==OSPATHSEP)
+ pLast=pPath+1;
+ pCur++;
+ pPath++;
+ }
+ if (*pPath==OSPATHSEP && !*pCur)
+ pLast=pPath+1;
+ string retPath;
+ if (*pCur==OSPATHSEP) {
+ bool first=true;
+ pCur++;
+ retPath="..";
+ while (*pCur)
+ {
+ if (*pCur==OSPATHSEP)
+ retPath+=OSPATHSEPSTR"..";
+ pCur++;
+ }
+ if (!pPath)
+ return retPath;
+ else
+ return retPath+OSPATHSEPSTR+pLast;
+ }
+ else
+ {
+ if (*pCur)
+ retPath=".."OSPATHSEPSTR;
+ while (*pCur)
+ {
+ if (*pCur==OSPATHSEP)
+ retPath+=".."OSPATHSEPSTR;
+ pCur++;
+ }
+ retPath+=pLast;
+ return retPath;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Make a path name relative to the curren directory
+string mhmakefileparser::f_relpath(const string & FileNames) const
+{
+ return IterList(FileNames,relpath);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string makeupper(const string &FileName,const string &)
+{
+ string Ret=FileName;
+ string::const_iterator pSrc=FileName.begin();
+ string::iterator pDest=Ret.begin();
+ while (pSrc!=FileName.end())
+ {
+ *pDest++ = toupper(*pSrc++);
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_toupper(const string & FileNames) const
+{
+ return IterList(FileNames,makeupper);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static string makelower(const string &FileName,const string &)
+{
+ string Ret=FileName;
+ string::const_iterator pSrc=FileName.begin();
+ string::iterator pDest=Ret.begin();
+ while (pSrc!=FileName.end())
+ {
+ *pDest++ = tolower(*pSrc++);
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::f_tolower(const string & FileNames) const
+{
+ return IterList(FileNames,makelower);
+}
+
diff --git a/tools/mhmake/src/md5.cpp b/tools/mhmake/src/md5.cpp
new file mode 100644
index 000000000..2161b3c75
--- /dev/null
+++ b/tools/mhmake/src/md5.cpp
@@ -0,0 +1,456 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/*
+ * RFC 1321 compliant MD5 implementation
+ *
+ * Copyright (C) 2001-2003 Christophe Devine
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "stdafx.h"
+#include "md5.h"
+
+#ifdef _MSC_VER
+#pragma warning (disable:4005) /* macro redefinition */
+#endif
+
+#ifdef _DEBUG
+map<uint32,string> g_Md5Database;
+#endif
+
+#define GET_UINT32(n,b,i) \
+{ \
+ (n) = ( (uint32) (b)[(i) ] ) \
+ | ( (uint32) (b)[(i) + 1] << 8 ) \
+ | ( (uint32) (b)[(i) + 2] << 16 ) \
+ | ( (uint32) (b)[(i) + 3] << 24 ); \
+}
+
+#define PUT_UINT32(n,b,i) \
+{ \
+ (b)[(i) ] = (uint8) ( (n) ); \
+ (b)[(i) + 1] = (uint8) ( (n) >> 8 ); \
+ (b)[(i) + 2] = (uint8) ( (n) >> 16 ); \
+ (b)[(i) + 3] = (uint8) ( (n) >> 24 ); \
+}
+
+void md5_starts( md5_context *ctx )
+{
+ ctx->total[0] = 0;
+ ctx->total[1] = 0;
+
+ ctx->state[0] = 0x67452301;
+ ctx->state[1] = 0xEFCDAB89;
+ ctx->state[2] = 0x98BADCFE;
+ ctx->state[3] = 0x10325476;
+#ifdef _DEBUG
+ if (g_BuildMd5Db) ctx->Data="";
+#endif
+}
+
+void md5_process( md5_context *ctx, uint8 data[64] )
+{
+ uint32 X[16], A, B, C, D;
+
+ GET_UINT32( X[0], data, 0 );
+ GET_UINT32( X[1], data, 4 );
+ GET_UINT32( X[2], data, 8 );
+ GET_UINT32( X[3], data, 12 );
+ GET_UINT32( X[4], data, 16 );
+ GET_UINT32( X[5], data, 20 );
+ GET_UINT32( X[6], data, 24 );
+ GET_UINT32( X[7], data, 28 );
+ GET_UINT32( X[8], data, 32 );
+ GET_UINT32( X[9], data, 36 );
+ GET_UINT32( X[10], data, 40 );
+ GET_UINT32( X[11], data, 44 );
+ GET_UINT32( X[12], data, 48 );
+ GET_UINT32( X[13], data, 52 );
+ GET_UINT32( X[14], data, 56 );
+ GET_UINT32( X[15], data, 60 );
+
+#define S(x,n) ((x << n) | ((x & 0xFFFFFFFF) >> (32 - n)))
+
+#define P(a,b,c,d,k,s,t) \
+{ \
+ a += F(b,c,d) + X[k] + t; a = S(a,s) + b; \
+}
+
+ A = ctx->state[0];
+ B = ctx->state[1];
+ C = ctx->state[2];
+ D = ctx->state[3];
+
+#define F(x,y,z) (z ^ (x & (y ^ z)))
+
+ P( A, B, C, D, 0, 7, 0xD76AA478 );
+ P( D, A, B, C, 1, 12, 0xE8C7B756 );
+ P( C, D, A, B, 2, 17, 0x242070DB );
+ P( B, C, D, A, 3, 22, 0xC1BDCEEE );
+ P( A, B, C, D, 4, 7, 0xF57C0FAF );
+ P( D, A, B, C, 5, 12, 0x4787C62A );
+ P( C, D, A, B, 6, 17, 0xA8304613 );
+ P( B, C, D, A, 7, 22, 0xFD469501 );
+ P( A, B, C, D, 8, 7, 0x698098D8 );
+ P( D, A, B, C, 9, 12, 0x8B44F7AF );
+ P( C, D, A, B, 10, 17, 0xFFFF5BB1 );
+ P( B, C, D, A, 11, 22, 0x895CD7BE );
+ P( A, B, C, D, 12, 7, 0x6B901122 );
+ P( D, A, B, C, 13, 12, 0xFD987193 );
+ P( C, D, A, B, 14, 17, 0xA679438E );
+ P( B, C, D, A, 15, 22, 0x49B40821 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (z & (x ^ y)))
+
+ P( A, B, C, D, 1, 5, 0xF61E2562 );
+ P( D, A, B, C, 6, 9, 0xC040B340 );
+ P( C, D, A, B, 11, 14, 0x265E5A51 );
+ P( B, C, D, A, 0, 20, 0xE9B6C7AA );
+ P( A, B, C, D, 5, 5, 0xD62F105D );
+ P( D, A, B, C, 10, 9, 0x02441453 );
+ P( C, D, A, B, 15, 14, 0xD8A1E681 );
+ P( B, C, D, A, 4, 20, 0xE7D3FBC8 );
+ P( A, B, C, D, 9, 5, 0x21E1CDE6 );
+ P( D, A, B, C, 14, 9, 0xC33707D6 );
+ P( C, D, A, B, 3, 14, 0xF4D50D87 );
+ P( B, C, D, A, 8, 20, 0x455A14ED );
+ P( A, B, C, D, 13, 5, 0xA9E3E905 );
+ P( D, A, B, C, 2, 9, 0xFCEFA3F8 );
+ P( C, D, A, B, 7, 14, 0x676F02D9 );
+ P( B, C, D, A, 12, 20, 0x8D2A4C8A );
+
+#undef F
+
+#define F(x,y,z) (x ^ y ^ z)
+
+ P( A, B, C, D, 5, 4, 0xFFFA3942 );
+ P( D, A, B, C, 8, 11, 0x8771F681 );
+ P( C, D, A, B, 11, 16, 0x6D9D6122 );
+ P( B, C, D, A, 14, 23, 0xFDE5380C );
+ P( A, B, C, D, 1, 4, 0xA4BEEA44 );
+ P( D, A, B, C, 4, 11, 0x4BDECFA9 );
+ P( C, D, A, B, 7, 16, 0xF6BB4B60 );
+ P( B, C, D, A, 10, 23, 0xBEBFBC70 );
+ P( A, B, C, D, 13, 4, 0x289B7EC6 );
+ P( D, A, B, C, 0, 11, 0xEAA127FA );
+ P( C, D, A, B, 3, 16, 0xD4EF3085 );
+ P( B, C, D, A, 6, 23, 0x04881D05 );
+ P( A, B, C, D, 9, 4, 0xD9D4D039 );
+ P( D, A, B, C, 12, 11, 0xE6DB99E5 );
+ P( C, D, A, B, 15, 16, 0x1FA27CF8 );
+ P( B, C, D, A, 2, 23, 0xC4AC5665 );
+
+#undef F
+
+#define F(x,y,z) (y ^ (x | ~z))
+
+ P( A, B, C, D, 0, 6, 0xF4292244 );
+ P( D, A, B, C, 7, 10, 0x432AFF97 );
+ P( C, D, A, B, 14, 15, 0xAB9423A7 );
+ P( B, C, D, A, 5, 21, 0xFC93A039 );
+ P( A, B, C, D, 12, 6, 0x655B59C3 );
+ P( D, A, B, C, 3, 10, 0x8F0CCC92 );
+ P( C, D, A, B, 10, 15, 0xFFEFF47D );
+ P( B, C, D, A, 1, 21, 0x85845DD1 );
+ P( A, B, C, D, 8, 6, 0x6FA87E4F );
+ P( D, A, B, C, 15, 10, 0xFE2CE6E0 );
+ P( C, D, A, B, 6, 15, 0xA3014314 );
+ P( B, C, D, A, 13, 21, 0x4E0811A1 );
+ P( A, B, C, D, 4, 6, 0xF7537E82 );
+ P( D, A, B, C, 11, 10, 0xBD3AF235 );
+ P( C, D, A, B, 2, 15, 0x2AD7D2BB );
+ P( B, C, D, A, 9, 21, 0xEB86D391 );
+
+#undef F
+
+ ctx->state[0] += A;
+ ctx->state[1] += B;
+ ctx->state[2] += C;
+ ctx->state[3] += D;
+}
+
+static uint8 md5_padding[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+void md5_update( md5_context *ctx, uint8 *input, uint32 length )
+{
+ uint32 left, fill;
+
+ if( ! length ) return;
+
+#ifdef _DEBUG
+ if (g_BuildMd5Db && md5_padding!=input && ctx->msglen!=input)
+ ctx->Data+=string((char*)input,length);
+#endif
+
+ left = ctx->total[0] & 0x3F;
+ fill = 64 - left;
+
+ ctx->total[0] += length;
+ ctx->total[0] &= 0xFFFFFFFF;
+
+ if( ctx->total[0] < length )
+ ctx->total[1]++;
+
+ if( left && length >= fill )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, fill );
+ md5_process( ctx, ctx->buffer );
+ length -= fill;
+ input += fill;
+ left = 0;
+ }
+
+ while( length >= 64 )
+ {
+ md5_process( ctx, input );
+ length -= 64;
+ input += 64;
+ }
+
+ if( length )
+ {
+ memcpy( (void *) (ctx->buffer + left),
+ (void *) input, length );
+ }
+}
+
+uint32 *md5_finishbin( md5_context *ctx)
+{
+ uint32 last, padn;
+ uint32 high, low;
+
+ high = ( ctx->total[0] >> 29 )
+ | ( ctx->total[1] << 3 );
+ low = ( ctx->total[0] << 3 );
+
+ PUT_UINT32( low, ctx->msglen, 0 );
+ PUT_UINT32( high, ctx->msglen, 4 );
+
+ last = ctx->total[0] & 0x3F;
+ padn = ( last < 56 ) ? ( 56 - last ) : ( 120 - last );
+
+ md5_update( ctx, md5_padding, padn );
+ md5_update( ctx, ctx->msglen, 8 );
+
+ return ctx->state;
+}
+
+void md5_finish( md5_context *ctx, uint8 digest[16] )
+{
+ md5_finishbin( ctx);
+ PUT_UINT32( ctx->state[0], digest, 0 );
+ PUT_UINT32( ctx->state[1], digest, 4 );
+ PUT_UINT32( ctx->state[2], digest, 8 );
+ PUT_UINT32( ctx->state[3], digest, 12 );
+}
+
+uint32 md5_finish32( md5_context *ctx)
+{
+ md5_finishbin( ctx);
+ uint32 Md5_32=ctx->state[0]+ctx->state[1]+ctx->state[2]+ctx->state[3];
+#ifdef _DEBUG
+ if (g_BuildMd5Db) g_Md5Database[Md5_32]=ctx->Data;
+#endif
+ return Md5_32;
+}
+
+#ifdef _DEBUG
+struct WRITEMD5DB
+{
+ ~WRITEMD5DB()
+ {
+ if (g_BuildMd5Db)
+ {
+ FILE *pFile=fopen("Md5.database","wb");
+ map<uint32,string>::const_iterator It=g_Md5Database.begin();
+ while (It!=g_Md5Database.end())
+ {
+ fprintf(pFile,"%08x: ",It->first);
+ fwrite(It->second.c_str(),It->second.length(),1,pFile);
+ fprintf(pFile,"\n");
+ It++;
+ }
+ fclose(pFile);
+ }
+ }
+};
+static WRITEMD5DB WriteMd5Db;
+#endif
+
+
+#ifdef TEST
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * those are the standard RFC 1321 test vectors
+ */
+
+static char *msg[] =
+{
+ "",
+ "a",
+ "abc",
+ "message digest",
+ "abcdefghijklmnopqrstuvwxyz",
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
+ "12345678901234567890123456789012345678901234567890123456789012" \
+ "345678901234567890"
+};
+
+static char *val[] =
+{
+ "d41d8cd98f00b204e9800998ecf8427e",
+ "0cc175b9c0f1b6a831c399e269772661",
+ "900150983cd24fb0d6963f7d28e17f72",
+ "f96b697d7cb7938d525a2f31aaf161d0",
+ "c3fcd3d76192e4007dfb496cca67e13b",
+ "d174ab98d277d9f5a5611c2c9f419d9f",
+ "57edf4a22be3c955ac49da2e2107b67a"
+};
+
+int main( int argc, char *argv[] )
+{
+ FILE *f;
+ int i, j;
+ char output[33];
+ md5_context ctx;
+ unsigned char buf[1000];
+ unsigned char md5sum[16];
+
+ if( argc < 2 )
+ {
+ printf( "\n MD5 Validation Tests:\n\n" );
+
+ for( i = 0; i < 7; i++ )
+ {
+ printf( " Test %d ", i + 1 );
+
+ md5_starts( &ctx );
+ md5_update( &ctx, (uint8 *) msg[i], strlen( msg[i] ) );
+ md5_finish( &ctx, md5sum );
+
+ for( j = 0; j < 16; j++ )
+ {
+ sprintf( output + j * 2, "%02x", md5sum[j] );
+ }
+
+ if( memcmp( output, val[i], 32 ) )
+ {
+ printf( "failed!\n" );
+ return( 1 );
+ }
+
+ printf( "passed.\n" );
+ }
+
+ printf( "\n" );
+ }
+ else
+ {
+ if( ! ( f = fopen( argv[1], "rb" ) ) )
+ {
+ perror( "fopen" );
+ return( 1 );
+ }
+
+ md5_starts( &ctx );
+
+ while( ( i = fread( buf, 1, sizeof( buf ), f ) ) > 0 )
+ {
+ md5_update( &ctx, buf, i );
+ }
+
+ md5_finish( &ctx, md5sum );
+
+ for( j = 0; j < 16; j++ )
+ {
+ printf( "%02x", md5sum[j] );
+ }
+
+ printf( " %s\n", argv[1] );
+ }
+
+ return( 0 );
+}
+
+#endif
+
+//#define APP
+#ifdef APP
+#include <stdio.h>
+
+int main( int argc, char *argv[] )
+{
+ unsigned j;
+ unsigned char md5sum[16];
+ md5_context ctx;
+ FILE *pFile=fopen(argv[1],"r");
+
+ if (!pFile)
+ {
+ printf("Error opening file.\n");
+ return 1;
+ }
+
+ md5_starts( &ctx );
+
+ while (1)
+ {
+ char Buf[1024];
+ size_t Ret=fread(Buf,1,sizeof(Buf),pFile);
+ if (!Ret)
+ break;
+ md5_update( &ctx, (uint8 *) Buf, Ret);
+ }
+ md5_finish( &ctx, md5sum );
+
+ for( j = 0; j < 16; j++ )
+ {
+ printf("%02x", md5sum[j] );
+ }
+
+}
+#endif
+
diff --git a/tools/mhmake/src/md5.h b/tools/mhmake/src/md5.h
new file mode 100644
index 000000000..5f604d460
--- /dev/null
+++ b/tools/mhmake/src/md5.h
@@ -0,0 +1,58 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef _MD5_H
+#define _MD5_H
+
+#ifndef uint8
+#define uint8 unsigned char
+#endif
+
+#ifndef uint32
+#define uint32 unsigned long int
+#endif
+
+typedef struct
+{
+ uint32 total[2];
+ uint32 state[4];
+ uint8 buffer[64];
+ uint8 msglen[8];
+#ifdef _DEBUG
+ string Data;
+#endif
+}
+md5_context;
+
+typedef uint32 md5_val[4];
+
+void md5_starts( md5_context *ctx );
+void md5_update( md5_context *ctx, uint8 *input, uint32 length );
+void md5_finish( md5_context *ctx, uint8 digest[16] );
+uint32 *md5_finishbin( md5_context *ctx);
+uint32 md5_finish32( md5_context *ctx);
+
+#ifdef _DEBUG
+extern bool g_BuildMd5Db;
+#endif
+
+#endif /* md5.h */
+
+
diff --git a/tools/mhmake/src/mhmake.cpp b/tools/mhmake/src/mhmake.cpp
new file mode 100644
index 000000000..7e9b117b2
--- /dev/null
+++ b/tools/mhmake/src/mhmake.cpp
@@ -0,0 +1,132 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+#include "fileinfo.h"
+#include "mhmakeparser.h"
+#include "rule.h"
+#include "util.h"
+
+bool g_Clean=false;
+
+int __CDECL main(int argc, char* argv[])
+{
+ //__int64 Freq;
+ //__int64 Start;
+
+ //QueryPerformanceFrequency((LARGE_INTEGER*)&Freq);
+ //QueryPerformanceCounter((LARGE_INTEGER*)&Start);
+
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
+
+ // Turn on leak-checking bit
+ tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
+ // Turn on defer freeing
+ //tmpFlag |= _CRTDBG_DELAY_FREE_MEM_DF;
+ // Turn on heap checking
+ //tmpFlag |= _CRTDBG_CHECK_ALWAYS_DF;
+
+ // Set flag to the new value
+ _CrtSetDbgFlag( tmpFlag );
+
+ //_CrtSetBreakAlloc(44);
+ #endif
+
+ try
+ {
+ mhmakefileparser::InitBuildTime();
+
+ putenv("PLATFORM="PLATFORM);
+
+ vector<string> CmdLineArgs;
+ for (int i=1; i<argc; i++)
+ {
+ CmdLineArgs.push_back(argv[i]);
+ }
+
+ refptr<loadedmakefile> pFirstMakefile(new loadedmakefile(CmdLineArgs,"makefile"));
+ // For the first makefile we add the defines passed on the command line to the
+ // environment so that the other load_makefile see the same variables
+ pFirstMakefile->AddCommandLineVarsToEnvironment();
+
+ g_LoadedMakefiles.push_back(pFirstMakefile);
+
+ pFirstMakefile->LoadMakefile();
+
+ #ifdef _DEBUG
+ if (g_PrintVarsAndRules)
+ {
+ DumpVarsAndRules();
+ }
+ #endif
+
+ // Make sure that the included makefiles that have rules are build
+
+ LOADEDMAKEFILES::iterator LoadMakIt=g_LoadedMakefiles.begin();
+ while (LoadMakIt!=g_LoadedMakefiles.end())
+ {
+ (*LoadMakIt)->m_pParser->BuildIncludedMakefiles();
+ (*LoadMakIt)->m_pParser->ParseBuildedIncludeFiles();
+ LoadMakIt++;
+ }
+
+ if (pFirstMakefile->m_CommandLineTargets.size())
+ {
+ vector<string>::iterator It=pFirstMakefile->m_CommandLineTargets.begin();
+ while (It!=pFirstMakefile->m_CommandLineTargets.end())
+ {
+ if (*It=="clean" || *It=="cleanall")
+ {
+ g_Clean=true;
+ }
+ pFirstMakefile->m_pParser->BuildTarget(GetFileInfo(*It,pFirstMakefile->m_MakeDir));
+ It++;
+ }
+ }
+ else
+ {
+ refptr<fileinfo> FirstTarget=pFirstMakefile->m_pParser->GetFirstTarget();
+ if (FirstTarget)
+ pFirstMakefile->m_pParser->BuildTarget(FirstTarget);
+ else
+ cout << "Warning: no targets in makefile. Nothing to be build.\nMHMAKECONF defined?\n";
+ }
+
+ }
+ catch (int Error)
+ {
+ printf("Error occured %d\n",Error);
+ #ifdef _DEBUG
+ if (g_DumpOnError)
+ {
+ DumpVarsAndRules();
+ }
+ #endif
+ return 1;
+ }
+
+ //__int64 Stop;
+ //QueryPerformanceCounter((LARGE_INTEGER*)&Stop);
+ //cout << (Stop-Start)*1000/Freq << endl;
+
+ return 0;
+}
+
diff --git a/tools/mhmake/src/mhmakefileparser.cpp b/tools/mhmake/src/mhmakefileparser.cpp
new file mode 100644
index 000000000..28ad0a682
--- /dev/null
+++ b/tools/mhmake/src/mhmakefileparser.cpp
@@ -0,0 +1,1086 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+#include "util.h"
+#include "md5.h"
+
+#include "mhmakefileparser.h"
+#include "rule.h"
+#include "mhmakelexer.h"
+
+///////////////////////////////////////////////////////////////////////////////
+int mhmakefileparser::yylex(void)
+{
+ m_yyloc=m_ptheLexer->m_Line;
+ return m_ptheLexer->yylex(m_theTokenValue);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::yyerror(char *m)
+{
+ cerr << this->m_ptheLexer->m_InputFileName<< " ("<<m_yyloc<<"): "<<m<<endl;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+int mhmakefileparser::ParseFile(const refptr<fileinfo> &FileInfo,bool SetMakeDir)
+{
+ mhmakelexer theLexer;
+ m_ptheLexer=&theLexer;
+ if (SetMakeDir)
+ {
+ m_MakeDir=curdir::GetCurDir();
+ m_Variables[CURDIR]=m_MakeDir->GetFullFileName();
+ }
+ theLexer.m_InputFileName=FileInfo->GetFullFileName();
+ theLexer.m_pParser=(mhmakeparser*)this;
+ theLexer.yyin=::fopen(FileInfo->GetFullFileName().c_str(),"r");
+ if (!theLexer.yyin)
+ {
+ cerr << "Error opening makefile: "<<FileInfo->GetFullFileName()<<endl;
+ return 1;
+ }
+ int Ret=yyparse();
+ ::fclose(theLexer.yyin);
+ return Ret;
+}
+
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsDefined(const string &Var) const
+{
+ bool Ret = m_Variables.find(Var)!=m_Variables.end();
+ if (!Ret)
+ {
+ string Env=GetFromEnv(Var);
+ if (!Env.empty())
+ {
+ Ret=true;
+ }
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static inline int SkipUntilQuote(const string &Expr,int i,char Char)
+{
+ while (Expr[i++]!=Char) ;
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+static inline int SkipMakeExpr(const string &Expr,int i)
+{
+ char Char=Expr[i++];
+ if (Char!='(')
+ return i;
+ Char=Expr[i++];
+ while (Char!=')')
+ {
+ if (Char=='$')
+ {
+ i=SkipMakeExpr(Expr,i);
+ }
+ Char=Expr[i++];
+ }
+ return i;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Splits expression on the Item, but the item may not occur within
+// a macro or quoted string
+static pair<string,string> SplitExpr(const string &Expr,char Item)
+{
+ int i=0;
+ char Char=Expr[i++];
+ while (Char!=Item)
+ {
+ if (Char=='"' || Char=='\'')
+ {
+ i=SkipUntilQuote(Expr,i,Char);
+ }
+ else if (Char=='$')
+ {
+ i=SkipMakeExpr(Expr,i);
+ }
+ Char=Expr[i++];
+ }
+ return pair<string,string>(Expr.substr(0,i-1),Expr.substr(i));
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool mhmakefileparser::IsEqual(const string &EqualExpr) const
+{
+ string Expr=ExpandExpression(EqualExpr);
+ const char *pStr=Expr.c_str();
+ const char *pTmp=pStr;
+ while (*pTmp && *pTmp!=',') pTmp++;
+ int Pos=pTmp-pStr;
+ int Size=Expr.size();
+ pTmp=pStr+Size-1;
+ while (pTmp>pStr && strchr(" \t",*pTmp))
+ {
+ pTmp--;
+ }
+ if (2*Pos != pTmp-pStr)
+ {
+ return false;
+ }
+ pTmp=pStr;
+ const char *pTmp2=pTmp+Pos+1;
+ if (*pTmp=='(')
+ {
+ pTmp++;
+ Pos--;
+ }
+ for (int i=0; i<Pos; i++)
+ {
+ if (pTmp[i]!=pTmp2[i])
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandExpression(const string &Expr) const
+{
+ ((mhmakefileparser*)this)->m_InExpandExpression++;
+ int i=0;
+ int Length=Expr.size();
+ string Ret;
+ bool DollarEscaped=false;
+ while (i<Length)
+ {
+ char Char=Expr[i++];
+ if (Char=='$')
+ {
+ char CharNext=Expr[i];
+ if (CharNext=='$')
+ {
+ Ret+="$$";
+ i++;
+ DollarEscaped=true;
+ }
+ else
+ {
+ int inew=SkipMakeExpr(Expr,i);
+ i++;
+ if (inew>i)
+ {
+ Ret+=ExpandMacro(Expr.substr(i,inew-i-1));
+ i=inew;
+ }
+ else
+ {
+ // This is a single character expression
+ Ret+=ExpandMacro(string(1,Expr[i-1]));
+ }
+ }
+ }
+ else
+ {
+ Ret+=Char;
+ }
+
+ }
+ // Here we do a special case in case we still have a $ without a %
+ if (m_InExpandExpression==1)
+ {
+ if (Ret.find('$')!=string::npos)
+ Ret=ExpandExpression(Ret);
+ if (DollarEscaped)
+ {
+ int Pos;
+ while ((Pos=Ret.find("$$"))!=string::npos)
+ {
+ Ret=Ret.replace(Pos,2,"$");
+ }
+ }
+ }
+ ((mhmakefileparser*)this)->m_InExpandExpression--;
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandMacro(const string &Expr) const
+{
+ if (Expr.find('%')!=string::npos && Expr.find('$')==string::npos && Expr.find(':')==string::npos)
+ return string("$(")+Expr+")";
+ string ExpandedExpr=ExpandExpression(Expr);
+
+ const char *pTmp=ExpandedExpr.c_str();
+ /* First remove leading spaces */
+ while (*pTmp==' ' || *pTmp=='\t') pTmp++;
+ const char *pVar=pTmp;
+ while (*pTmp && *pTmp!=' ' && *pTmp!='\t' && *pTmp!=':') pTmp++;
+ const char *pVarEnd=pTmp;
+ char Type=*pTmp++;
+ while (*pTmp && (*pTmp==' ' || *pTmp=='\t')) pTmp++;
+ if (Type&&*pTmp)
+ { // We have a match for the regular expression ^([^ \\t:]+)([: \\t])[ \\t]*(.+)
+ if (Type==':')
+ {
+ string ToSubst=ExpandExpression(ExpandVar(string(pVar,pVarEnd)));
+ const char *pSrc=pTmp;
+ const char *pStop=pSrc;
+ while (*pStop!='=') pStop++;
+ const char *pTo=pStop+1;
+ string SrcStr(pSrc,pStop);
+ string ToStr(pTo);
+ return Substitute(ToSubst,SrcStr,ToStr);
+ }
+ else if (Type==' ' || Type == '\t')
+ {
+ string Func(pVar,pVarEnd);
+ string Arg(pTmp);
+ if (Arg.find('%')!=string::npos && Arg.find('$')!=string::npos)
+ return string("$(")+ExpandedExpr+")";
+ function_f pFunc=m_Functions[Func];
+ #ifdef _DEBUG
+ if (pFunc)
+ {
+ return (this->*pFunc)(Arg);
+ }
+ else
+ {
+ cerr << "Unknown function specified in macro: "<<Func<<endl;
+ throw 1;
+ }
+ #else
+ return (this->*pFunc)(Arg);
+ #endif
+ }
+ else
+ {
+ #ifdef _DEBUG
+ cerr << "Fatal error in ExpandMacro (bug in mhmake ? ? ?)";
+ throw 1;
+ #else
+ return g_EmptyString;
+ #endif
+ }
+ }
+ else
+ {
+ return ExpandExpression(ExpandVar(ExpandedExpr));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string mhmakefileparser::ExpandVar(const string &Var) const
+{
+ map<string,string>::const_iterator pIt=m_Variables.find(Var);
+ if (pIt==m_Variables.end())
+ {
+ if (Var.size()==1)
+ {
+ char Char=Var[0];
+ if (m_RuleThatIsBuild)
+ {
+ switch (Char)
+ {
+ case '<': // return first prerequisit
+#ifdef _DEBUG
+ if (!m_RuleThatIsBuild->GetDeps().size())
+ {
+ return "<No Dependencies defined.>";
+ }
+#endif
+ return m_RuleThatIsBuild->GetDeps()[0]->GetFullFileName();
+ case '@': // return full target file name
+ return m_RuleThatIsBuild->GetFullFileName();
+ case '*': // return stem
+ return m_RuleThatIsBuild->GetRule()->GetStem();
+ case '^': // return all prerequisits
+ return m_RuleThatIsBuild->GetPrerequisits();
+ case '/':
+ return OSPATHSEPSTR;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ switch (Char)
+ {
+ case '<': // return first prerequisit
+ case '@': // return full target file name
+ case '*': // return stem
+ case '^': // return all prerequisits
+ return Var; // To make comparing of rules more accurate
+ case '/':
+ return OSPATHSEPSTR;
+ default:
+ break;
+ }
+ }
+ }
+ string Env=GetFromEnv(Var);
+ if (Env.empty())
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ {
+ cout<<"Warning: Variable "<<Var<<" not found\n";
+ }
+ #endif
+ return g_EmptyString;
+ }
+ else
+ return Env;
+ }
+ else
+ return pIt->second;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void SplitToItems(const string &String,vector< refptr<fileinfo> > &Items,refptr<fileinfo> Dir)
+{
+ const char *pTmp=String.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ if (!Item.empty())
+ {
+ if (Item[0]=='"')
+ {
+ Item=Item.substr(1,Item.size()-2);
+ }
+ Items.push_back(GetFileInfo(Item,Dir));
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::ParseBuildedIncludeFiles()
+{
+ vector<string>::iterator It=m_ToBeIncludeAfterBuild.begin();
+ while (It!=m_ToBeIncludeAfterBuild.end())
+ {
+ int result=ParseFile(GetFileInfo(*It));
+ if (result)
+ {
+ fprintf(stderr,"Error parsing %s\n",It->c_str());
+ throw(1);
+ }
+ It++;
+ }
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::PrintVariables(bool Expand) const
+{
+ map<string,string>::const_iterator It=m_Variables.begin();
+ while (It!=m_Variables.end())
+ {
+ if (Expand)
+ {
+ try
+ {
+ cout<<It->first<<" : "<<ExpandExpression(It->second)<<endl;
+ }
+ catch (...)
+ {
+ cout<<endl;
+ }
+ }
+ else
+ {
+ cout<<It->first<<" : "<<It->second<<endl;
+ }
+ It++;
+ }
+ cout << "Exported Variables:\n";
+ vector<string>::const_iterator It2=m_Exports.begin();
+ while (It2!=m_Exports.end())
+ {
+ cout<<*It2<<endl;
+ It2++;
+ }
+}
+#endif
+
+//#define PAGETOSTRING(Nr) #Nr
+//#define PAGETONBR(Nr) PAGETOSTRING(##Nr)
+//#pragma message("ar=" PAGETONBR(NULL) ";")
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::AddRule()
+{
+ vector< refptr<fileinfo> >::iterator pIt=m_pCurrentItems->begin();
+ while (pIt!=m_pCurrentItems->end())
+ {
+ if ((*pIt)->GetFullFileName().find('%')!=string::npos)
+ {
+ IMPLICITRULE::AddImplicitRule(*pIt,*m_pCurrentDeps,m_pCurrentRule);
+ }
+ else
+ {
+ // If we had a double colon we must make sure that the target is always build
+ if (m_DoubleColonRule)
+ {
+ (*pIt)->SetNotExist();
+ }
+ (*pIt)->SetRuleIfNotExist(m_pCurrentRule);
+ if (!m_pCurrentRule)
+ (*pIt)->AddDeps(*m_pCurrentDeps);
+ else
+ (*pIt)->InsertDeps(*m_pCurrentDeps);
+ if (!m_FirstTarget)
+ { // Only check this if the rule is not an implicit rule
+ m_FirstTarget=(*m_pCurrentItems)[0];
+ }
+ }
+ pIt++;
+ }
+ m_pCurrentItems=NULL;
+ m_pCurrentRule=NullRule;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::GetAutoDeps(const refptr<fileinfo> &FirstDep,set< refptr<fileinfo> > &Autodeps)
+{
+ /* Here we have to scan only c/c++ headers so skip certain extensions */
+ const char *pFullName=FirstDep->GetFullFileName().c_str();
+ const char *pExt=strrchr(pFullName,'.');
+ bool bPython=false;
+ if (pExt)
+ {
+ if (!_stricmp(pExt+1,"py"))
+ bPython=true;
+ }
+
+ FILE *pIn=fopen(pFullName,"r");
+ if (!pIn)
+ return;
+
+ refptr<fileinfo> CurDir=curdir::GetCurDir(); /* Since we are calling BuildTarget, the current directory might change, which is not what we should expecting */
+ char IncludeList[255];
+ int PrevRet=0;
+ int Ret=fgetc(pIn);
+ while(Ret!=EOF)
+ {
+ char Type[2];
+ bool bFound=false;
+ if (bPython)
+ {
+ Type[0]='"';
+ if (Ret=='i')
+ {
+ Ret=fscanf(pIn,"mport %254[^\"\n]",IncludeList);
+ if (Ret==1)
+ {
+ if (IncludeList[0]!='*')
+ bFound=true;
+ }
+ }
+ }
+ else
+ {
+ if (PrevRet=='/')
+ {
+ if (Ret=='/')
+ {
+ /* This is a C++ command so read until the next line-feed */
+ do
+ {
+ Ret=fgetc(pIn);
+ } while (Ret!='\n' && Ret!=EOF);
+ }
+ else if (Ret=='*')
+ {
+ /* This is a standard C comment, so read until then end of the command */
+ do
+ {
+ PrevRet=Ret;
+ Ret=fgetc(pIn);
+ } while ((PrevRet!='*' || Ret!='/') && Ret!=EOF);
+ }
+ }
+ else if (Ret=='#' || Ret=='.')
+ {
+ if (Ret=='#')
+ Ret=fscanf(pIn,"include %1[\"<]%254[^>\"]%*[\">]",&Type,IncludeList);
+ else
+ Ret=fscanf(pIn,"import %1[\"<]%254[^>\"]%*[\">]",&Type,IncludeList);
+ if (Ret==2)
+ {
+ bFound=true;
+ }
+ }
+ }
+ if (bFound)
+ {
+ const char *pTmp=IncludeList;
+ while (*pTmp)
+ {
+ string IncludeFile;
+ pTmp=NextItem(pTmp,IncludeFile," \t,");
+ if (bPython)
+ IncludeFile+=".py";
+
+ if (SkipHeaderFile(IncludeFile))
+ continue;
+ #ifdef _DEBUG
+ m_ImplicitSearch++; // This is to avoid warnings of targets that does not exist
+ #endif
+ if (Type[0]=='"')
+ {
+ refptr<fileinfo> pInclude=GetFileInfo(IncludeFile,FirstDep->GetDir());
+ /* Add the dependency when the file alrady exist or there is a rule available to be build */
+ if (BuildTarget(pInclude).DoesExist()) // Try to build the target, and add it if it exists after building
+ {
+ set< refptr<fileinfo> >::iterator pFind=Autodeps.find(pInclude);
+ if (pFind==Autodeps.end())
+ {
+ Autodeps.insert(pInclude);
+ GetAutoDeps(pInclude,Autodeps);
+ }
+ }
+ }
+ const refptr<fileinfoarray> IncludeDirs=GetIncludeDirs();
+ vector< refptr<fileinfo> >::const_iterator It=IncludeDirs->begin();
+ while (It<IncludeDirs->end())
+ {
+ refptr<fileinfo> pInclude=GetFileInfo(IncludeFile,*It);
+ if (BuildTarget(pInclude).DoesExist()) // Try to build the target, and add it if it exists after building
+ {
+ set< refptr<fileinfo> >::iterator pFind=Autodeps.find(pInclude);
+ if (pFind==Autodeps.end())
+ {
+ Autodeps.insert(pInclude);
+ GetAutoDeps(pInclude,Autodeps);
+ }
+ break;
+ }
+ It++;
+ }
+ #ifdef _DEBUG
+ m_ImplicitSearch--;
+ #endif
+ }
+ }
+ PrevRet=Ret;
+ Ret=fgetc(pIn);
+ }
+ fclose(pIn);
+ curdir::ChangeCurDir(CurDir);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::UpdateAutomaticDependencies(const refptr<fileinfo> &Target)
+{
+ m_AutoDepsDirty=true; /* Always assume dirty since in the autodeps file, the md5 strings are also saved. */
+ const char *pName=Target->GetFullFileName().c_str();
+ const char *pExt=strrchr(pName,'.');
+ if (!pExt)
+ return;
+ if (Target->IsAutoDepExtention())
+ {
+ // we have to search for the include files in the first dependency of Target
+ vector< refptr<fileinfo> > &Deps=Target->GetDeps();
+ if (!Deps.size())
+ return; // There is no first dep
+ refptr<fileinfo> FirstDep=Deps[0];
+
+ set< refptr<fileinfo> > &Autodeps=m_AutoDeps[Target];
+ Autodeps.clear(); // We are going to scan again, so clear the list
+ GetAutoDeps(FirstDep,Autodeps);
+ // Now add these dependencies also to the rules
+ set< refptr<fileinfo> >::iterator It=Autodeps.begin();
+ while (It!=Autodeps.end())
+ {
+ Target->AddDep(*It);
+ It++;
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+const refptr<fileinfoarray> mhmakefileparser::GetIncludeDirs() const
+{
+ string Includes=ExpandExpression("$(INCLUDES)");
+ if (!m_pIncludeDirs || Includes != m_IncludeDirs)
+ {
+ ((mhmakefileparser*)this)->m_IncludeDirs=Includes;
+ ((mhmakefileparser*)this)->m_pIncludeDirs=refptr<fileinfoarray>(new fileinfoarray);
+ if (Includes.empty()) // If not defined try a default path
+ Includes="include inc .."OSPATHSEPSTR"include .."OSPATHSEPSTR"inc";
+ const char *pTmp=Includes.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ refptr<fileinfo> pIncDir=GetFileInfo(Item,m_MakeDir);
+ if (pIncDir->Exists() || pIncDir->GetRule())
+ ((mhmakefileparser*)this)->m_pIncludeDirs->push_back(pIncDir);
+ }
+ }
+ return m_pIncludeDirs;
+}
+
+static void ReadStr(FILE *pFile,char *Str)
+{
+ int i=0;
+ while (1)
+ {
+ Str[i]=fgetc(pFile);
+#ifdef _DEBUG
+ if (Str[i]==EOF)
+ {
+ cout<<"Premature end of depency file.\n";
+ Str[i]=0;
+ return;
+ }
+#endif
+ if (Str[i]=='\n')
+ break;
+ i++;
+ }
+ Str[i]='\0';
+}
+
+void mhmakefileparser::LoadAutoDepsFile(refptr<fileinfo> &DepFile)
+{
+ if (m_AutoDepFileLoaded && m_AutoDepFileLoaded==DepFile)
+ return; /* This autodep file is already loaded. */
+
+ m_AutoDepFileLoaded=DepFile;
+
+ FILE *pIn=fopen(DepFile->GetFullFileName().c_str(),"rb");
+#ifdef _DEBUG
+ if (!pIn)
+ {
+ cerr << "Error opening autodep file "<<DepFile->GetFullFileName()<<endl;
+ return;
+ }
+#endif
+ fread(&m_EnvMd5_32,sizeof(m_EnvMd5_32),1,pIn);
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Reading Env Md5 from "<<DepFile->GetFullFileName()<<": "<<hex<<m_EnvMd5_32<<endl;
+#endif
+ char UsedEnvVars[1024];
+ ReadStr(pIn,UsedEnvVars);
+ SetVariable(USED_ENVVARS,UsedEnvVars);
+
+ char FileName[MAX_PATH];
+ ReadStr(pIn,FileName);
+ while (FileName[0])
+ {
+ refptr<fileinfo> Target=GetFileInfo(FileName);
+ set< refptr<fileinfo> > &Autodeps=m_AutoDeps[Target];
+ ReadStr(pIn,FileName);
+ while (FileName[0])
+ {
+ if (!g_ForceAutoDepRescan) /* If we are forcing the autodepscan we do not have to load the dependencies. */
+ {
+ refptr<fileinfo> Dep=GetFileInfo(FileName);
+ Autodeps.insert(Dep);
+ Target->AddDep(Dep);
+ }
+ ReadStr(pIn,FileName);
+ }
+ ReadStr(pIn,FileName);
+ }
+
+ uint32 Md5_32;
+ while (fread(&Md5_32,sizeof(Md5_32),1,pIn))
+ {
+ ReadStr(pIn,FileName);
+ pair <set<refptr<fileinfo>,less_refptrfileinfo >::iterator, bool> pPair=g_FileInfos.insert(new fileinfo(FileName,Md5_32));
+ if (!pPair.second)
+ {
+ #ifdef _DEBUG
+ if (!(*pPair.first)->CompareMd5_32(Md5_32) && !(*pPair.first)->CompareMd5_32(0))
+ cout << "Warning: trying to set to different md5's for Target "<<(*pPair.first)->GetFullFileName()<<" Old: "<<hex<<(*pPair.first)->GetCommandsMd5_32()<<" New: "<<Md5_32<<endl;
+ if (g_PrintAdditionalInfo)
+ cout << "Setting Md5 for Target "<<(*pPair.first)->GetFullFileName()<<" to "<<hex<<Md5_32<<endl;
+ #endif
+ (*pPair.first)->SetCommandsMd5_32(Md5_32); // If it was already there, just update the md5 value
+ }
+ AddTarget(*pPair.first);
+ }
+
+ fclose(pIn);
+}
+
+static void MakeDirs(const refptr<fileinfo> & Dir)
+{
+ refptr<fileinfo> ParentDir=Dir->GetDir();
+ if (!ParentDir->GetDate().DoesExist())
+ { /* First make parent dirs */
+ MakeDirs(ParentDir);
+ }
+ if (!Dir->GetDate().DoesExist())
+ { /* Create directory */
+ mkdir(Dir->GetFullFileName().c_str(),S_IRWXU);
+ }
+}
+
+void mhmakefileparser::SaveAutoDepsFile()
+{
+ if (!m_AutoDepsDirty)
+ return;
+
+ if (g_Clean
+#ifdef _DEBUG
+ || g_DoNotExecute || g_GenProjectTree
+#endif
+ )
+ return; // do not save on clean or if no commands are executed
+
+ string DepFile=ExpandVar(AUTODEPFILE);
+ if (!DepFile.size())
+ {
+ return;
+ }
+
+#ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout<<"Saving automatic dependency file "<<DepFile<<endl;
+#endif
+
+ FILE *pOut=fopen(DepFile.c_str(),"wb");
+ if (!pOut)
+ {
+ /* Maybe it is because the directory does not exist, so try to create this first */
+ MakeDirs(GetAbsFileInfo(DepFile.substr(0,DepFile.find_last_of(OSPATHSEP))));
+ pOut=fopen(DepFile.c_str(),"wb");
+
+ if (!pOut)
+ {
+ #ifdef _DEBUG
+ if (!g_DoNotExecute)
+ #endif
+ cerr << "Error creating file "<<DepFile<<endl;
+ return;
+ }
+ }
+ // First update the USER_ENVVARS variable and then save it to the dep file together with the md5 string
+ CreateUSED_ENVVARS();
+ uint32 Md5_32=CreateEnvMd5_32();
+ fwrite(&Md5_32,sizeof(Md5_32),1,pOut);
+ fprintf(pOut,"%s\n",m_Variables[USED_ENVVARS].c_str());
+
+ map< refptr<fileinfo>, set< refptr<fileinfo> > >::const_iterator It=m_AutoDeps.begin();
+ while (It!=m_AutoDeps.end())
+ {
+ fprintf(pOut,"%s\n",It->first->GetFullFileName().c_str());
+ set< refptr<fileinfo> >::const_iterator DepIt=It->second.begin();
+ while (DepIt!=It->second.end())
+ {
+ fprintf(pOut,"%s\n",(*DepIt)->GetFullFileName().c_str());
+ DepIt++;
+ }
+ fprintf(pOut,"\n");
+ It++;
+ }
+ /* Now save the Md5 strings */
+ fprintf(pOut,"\n");
+
+ set< const fileinfo * , less_fileinfo>::iterator pIt=m_Targets.begin();
+ while (pIt!=m_Targets.end())
+ {
+ if (!(*pIt)->CompareMd5_32(0))
+ {
+ (*pIt)->WriteMd5_32(pOut);
+ string FileName=(*pIt)->GetFullFileName();
+ fwrite(FileName.c_str(),FileName.size(),1,pOut);
+ fputc('\n',pOut);
+ }
+ pIt++;
+ }
+
+ fclose(pOut);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Uses the SKIPHEADERS variable to check if we have to skip the header in
+// the automatic dependency scanning
+
+bool mhmakefileparser::SkipHeaderFile(const string &FileName)
+{
+ if (!m_SkipHeadersInitialized)
+ {
+ m_SkipHeadersInitialized=true;
+ string HeadersToSkip=ExpandVar(SKIPHEADERS);
+ const char *pTmp=HeadersToSkip.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ if (Item.find('%')==string::npos)
+ {
+ m_SkipHeaders.insert(Item);
+ }
+ else
+ {
+ m_PercentHeaders.push_back(Item);
+ }
+ }
+ }
+ if (m_SkipHeaders.find(FileName)!=m_SkipHeaders.end())
+ return true;
+
+ vector<string>::const_iterator It=m_PercentHeaders.begin();
+ vector<string>::const_iterator ItEnd=m_PercentHeaders.end();
+ while (It!=ItEnd)
+ {
+ if (PercentMatch(FileName,*It++))
+ return true;
+ }
+
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+
+const mhmakefileparser *mhmakefileparser::m_spCurEnv; // Identifies which makefiles exports are currently set
+
+void mhmakefileparser::InitEnv() const
+{
+ if (this!=m_spCurEnv)
+ {
+ /* First restore the previous set environment variables */
+ if (m_spCurEnv)
+ m_spCurEnv->RestoreEnv();
+
+ ((mhmakefileparser*)this)->SaveEnv();
+ vector<string>::const_iterator It=m_Exports.begin();
+ vector<string>::const_iterator ItEnd=m_Exports.end();
+ while (It!=ItEnd)
+ {
+ putenv((char *)It->c_str());
+ It++;
+ }
+ m_spCurEnv=this;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::SaveEnv()
+{
+ vector<string>::const_iterator It=m_Exports.begin();
+ vector<string>::const_iterator ItEnd=m_Exports.end();
+ while (It!=ItEnd)
+ {
+ string Export=*It++;
+ const char *pTmp=Export.c_str();
+ string Var;
+ NextItem(pTmp,Var,"=");
+ const char *pEnv=getenv(Var.c_str());
+ string Env;
+ if (pEnv)
+ Env=pEnv;
+ m_SavedExports[Var]=Env;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void mhmakefileparser::RestoreEnv() const
+{
+ if (m_spCurEnv && this!=m_spCurEnv)
+ {
+ m_spCurEnv->RestoreEnv(); // Make sure that environment variable that were set in the other makefile (m_spCurEnv) are cleared, otherwise the environment is not completely restored.
+ }
+
+ map<string,string>::const_iterator It=m_SavedExports.begin();
+ map<string,string>::const_iterator ItEnd=m_SavedExports.end();
+ while (It!=ItEnd)
+ {
+#ifdef WIN32
+ char Env[1024];
+ sprintf(Env,"%s=%s",It->first.c_str(),It->second.c_str());
+ putenv(Env);
+#else
+ /* on linux, only use putenv if you know the supplied string will stay in memory */
+ setenv(It->first.c_str(),It->second.c_str(),1);
+#endif
+ It++;
+ }
+ ((mhmakefileparser*)this)->m_SavedExports.clear();
+ m_spCurEnv=NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+//
+//Create a Md5 string from m_GlobalCommandLineVars and USED_ENVVARS
+//
+
+
+static bool SkipVar(const string &Var)
+{
+ if (Var==WC_REVISION)
+ return true;
+ if (Var==WC_URL)
+ return true;
+ if (Var==MAKE)
+ return true;
+ return false;
+}
+
+#define DBGOUT(stuff)
+
+uint32 mhmakefileparser::CreateEnvMd5_32() const
+{
+ md5_context ctx;
+ string Md5;
+ string EnvVars=ExpandVar(USED_ENVVARS);
+ const char *pTmp=EnvVars.c_str();
+ map<string,string> Variables;
+
+ RestoreEnv(); // Restore the environment before creating the md5, so that we have the original environment when the makefile was parsed.
+
+ /* First create a list of variables to put in the md5 string. This is needed because a variable could be in the
+ * environment and passed on the command-line. This is especially important when switching environments were a certain
+ * variable sometimes is passed with the environment and sometimes on the command line */
+
+ while (*pTmp)
+ {
+ string Var;
+ pTmp=NextItem(pTmp,Var,";");
+ if (!SkipVar(Var))
+ {
+ string Val=GetFromEnv(Var,false);
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ transform(Val.begin(),Val.end(),Val.begin(),(int(__CDECL *)(int))toupper);
+ DBGOUT(cout << GetMakeDir()->GetFullFileName() << " -> Setting GetFromEnv var " << Var << " to " << Val << endl);
+ Variables[Var]=Val;
+ }
+ }
+ map<string,string>::const_iterator ItEnd=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.end();
+ map<string,string>::const_iterator It=loadedmakefile::sm_Statics.m_GlobalCommandLineVars.begin();
+ while (It!=ItEnd)
+ {
+ if (!SkipVar(It->first))
+ {
+ string Var=It->first;
+ transform(Var.begin(),Var.end(),Var.begin(),(int(__CDECL *)(int))toupper);
+ string Val=It->second;
+ transform(Val.begin(),Val.end(),Val.begin(),(int(__CDECL *)(int))toupper);
+ DBGOUT(cout << GetMakeDir()->GetFullFileName() << " -> Setting Commandline var " << Var << " to " << Val << endl);
+ Variables[Var]=Val;
+ }
+ It++;
+ }
+
+ // Now create the md5 string
+ md5_starts( &ctx );
+
+ DBGOUT(cout << "MD5 of " << m_MakeDir->GetFullFileName() << endl);
+
+ map<string,string>::const_iterator VarIt=Variables.begin();
+ map<string,string>::const_iterator VarItEnd=Variables.end();
+ while (VarIt!=VarItEnd)
+ {
+ md5_update( &ctx, (uint8 *) VarIt->first.c_str(), (unsigned long)VarIt->first.size());
+
+ if (!VarIt->second.empty())
+ {
+ md5_update( &ctx, (uint8 *) "=", 1);
+ md5_update( &ctx, (uint8 *) VarIt->second.c_str(), (unsigned long)VarIt->second.size());
+ DBGOUT(cout << VarIt->first << "=" << VarIt->second << endl);
+ }
+ DBGOUT(else cout << VarIt->first << endl;)
+ VarIt++;
+ }
+
+ return md5_finish32( &ctx);
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+
+bool mhmakefileparser::CompareEnv() const
+{
+ uint32 Md5_32=CreateEnvMd5_32();
+#ifdef _DEBUG
+ if (!g_GenProjectTree && Md5_32!=m_EnvMd5_32)
+ cout << "Environment has been changed: "<<hex<<m_EnvMd5_32<<" to "<<Md5_32<<endl;
+#endif
+ return Md5_32!=m_EnvMd5_32;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Makes sure that the makefile exports are set in the environment
+mh_time_t mhmakefileparser::m_sBuildTime;
+
+void mhmakefileparser::InitBuildTime()
+{
+#ifdef WIN32
+ FILETIME FileTime;
+ GetSystemTimeAsFileTime(&FileTime);
+ m_sBuildTime=g_ZeroTime.ConvertTime(&FileTime);
+#else
+ m_sBuildTime=time(NULL);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Returns a variable from the environment or from the command line and adds it the m_UsedEnvVars
+string mhmakefileparser::GetFromEnv(const string &Var, bool Cache) const
+{
+ /* First we look into the command line variables, before we are looking in the environment */
+ map<string,string>::const_iterator pLineFind=m_CommandLineVars.find(Var);
+ if (pLineFind!=m_CommandLineVars.end())
+ {
+ ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
+ if (Cache)
+ ((mhmakefileparser*)this)->m_Variables[Var]=pLineFind->second;
+ return pLineFind->second;
+ }
+
+ const char *pEnv=getenv(Var.c_str());
+ if (!pEnv)
+ {
+ return g_EmptyString;
+ }
+ ((mhmakefileparser*)this)->m_UsedEnvVars.insert(Var);
+
+ if (Cache)
+ ((mhmakefileparser*)this)->m_Variables[Var]=pEnv;
+ return pEnv;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Creates the variable USER_ENVVARS to be saved in the autodeps file
+//
+
+void mhmakefileparser::CreateUSED_ENVVARS()
+{
+ set<string>::const_iterator It=m_UsedEnvVars.begin();
+ set<string>::const_iterator ItEnd=m_UsedEnvVars.end();
+ string Val;
+ while (It!=ItEnd)
+ {
+ Val+=*It;
+ Val+=";";
+ It++;
+ }
+ m_Variables[USED_ENVVARS]=Val;
+}
+
diff --git a/tools/mhmake/src/mhmakefileparser.h b/tools/mhmake/src/mhmakefileparser.h
new file mode 100644
index 000000000..9d41c393d
--- /dev/null
+++ b/tools/mhmake/src/mhmakefileparser.h
@@ -0,0 +1,279 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __MHMAKEFILE_PARSER__
+#define __MHMAKEFILE_PARSER__
+
+#include "fileinfo.h"
+#include "util.h"
+
+class rule;
+
+class mhmakelexer;
+
+struct TOKENVALUE
+{
+ string theString;
+ int ival;
+};
+
+class mhmakefileparser;
+typedef string (mhmakefileparser::*function_f)(const string &) const;
+
+struct funcdef
+{
+ const char *szFuncName;
+ function_f pFunc;
+};
+
+class fileinfoarray : public refbase,public vector<refptr<fileinfo> >
+{
+};
+
+class mhmakefileparser : public refbase
+{
+
+private:
+ mhmakelexer *m_ptheLexer;
+ int m_yyloc;
+ refptr<fileinfo> m_RuleThatIsBuild;
+ vector<string> m_ToBeIncludeAfterBuild;
+ vector<string> m_MakefilesToLoad;
+ refptr<fileinfo> m_AutoDepFileLoaded;
+ int m_InExpandExpression;
+ mh_time_t m_Date;
+ uint32 m_EnvMd5_32; /* Cached Md5_32 value of the userd environment variables */
+#ifdef _DEBUG
+ int m_ImplicitSearch;
+#endif
+ map<string,string> m_CommandCache;
+
+protected:
+ map<string,string> m_Variables;
+ map<string,string> m_CommandLineVars;
+ TOKENVALUE m_theTokenValue;
+ refptr<fileinfo> m_MakeDir;
+ refptr<rule> m_pCurrentRule;
+ refptr<fileinfoarray> m_pCurrentItems;
+ refptr<fileinfoarray> m_pCurrentDeps;
+ refptr<fileinfo> m_FirstTarget;
+ fileinfoarray m_IncludedMakefiles;
+ refptr< fileinfoarray > m_pIncludeDirs;
+ string m_IncludeDirs;
+ map< refptr<fileinfo>, set< refptr<fileinfo> > > m_AutoDeps;
+ set< const fileinfo* , less_fileinfo > m_Targets; // List of targets that are build by this makefile
+ bool m_DoubleColonRule;
+ bool m_AutoDepsDirty;
+ bool m_ForceAutoDepRescan;
+ set<string> m_SkipHeaders; // Headers to skip
+ vector<string> m_PercentHeaders; // Percent specification of headers to skip
+ bool m_SkipHeadersInitialized; // true when previous 2 variables are initialized
+
+ bool m_RebuildAll; /* true when to rebuild all targets of this makefile */
+
+ vector<string> m_Exports; // Array of variables to export.
+ map<string,string> m_SavedExports; // Original values of the environment are saved here.
+ set<string> m_UsedEnvVars; // Array containing a list of variables that are taken from the environment (This is used for rebuild checking)
+ static const mhmakefileparser *m_spCurEnv; // Identifies which makefiles exports are currently set
+
+ static mh_time_t m_sBuildTime;
+
+public:
+#ifdef _DEBUG
+ deque< refptr<fileinfo> > m_TargetStack; /* Used to detect circular dependencies */
+#endif
+
+ mhmakefileparser(const map<string,string> &CommandLineVars)
+ : m_CommandLineVars(CommandLineVars)
+ ,m_InExpandExpression(0)
+ ,m_AutoDepsDirty(false)
+ ,m_ForceAutoDepRescan(false)
+ ,m_SkipHeadersInitialized(false)
+ ,m_RebuildAll(false)
+ ,m_EnvMd5_32(0)
+ #ifdef _DEBUG
+ ,m_ImplicitSearch(false)
+ #endif
+ {
+ if (!m_FunctionsInitialised) InitFuncs();
+ SetVariable("MAKE_VERSION",MHMAKEVER);
+ SetVariable(OBJEXTVAR,OBJEXT);
+ SetVariable(EXEEXTVAR,EXEEXT);
+ }
+
+ bool CompareEnv() const;
+ uint32 CreateEnvMd5_32() const;
+ string GetFromEnv(const string &Var,bool Cache=true) const;
+ void CreateUSED_ENVVARS();
+
+ void SaveEnv();
+ void RestoreEnv() const;
+
+ void SetRebuildAll()
+ {
+ m_RebuildAll=true;
+ m_AutoDepsDirty=true; /* This is to be sure that all new calculated md5 strings are saved. */
+ }
+
+ void SetVariable(string Var,string Val)
+ {
+ m_Variables[Var]=Val;
+ }
+ void EnableAutoDepRescan(void)
+ {
+ m_ForceAutoDepRescan=true;
+ m_AutoDepsDirty=true;
+ }
+ bool ForceAutoDepRescan(void)
+ {
+ return m_ForceAutoDepRescan;
+ }
+ void SetRuleThatIsBuild(const refptr<fileinfo> &Target)
+ {
+ m_RuleThatIsBuild=Target;
+ }
+ void ClearRuleThatIsBuild()
+ {
+ m_RuleThatIsBuild=NULL;
+ }
+ void UpdateAutomaticDependencies(const refptr<fileinfo> &Target);
+ const refptr<fileinfoarray> GetIncludeDirs() const;
+ void GetAutoDeps(const refptr<fileinfo> &FirstDep,set< refptr<fileinfo> > &Autodeps);
+ void SaveAutoDepsFile();
+ void LoadAutoDepsFile(refptr<fileinfo> &DepFile);
+ bool SkipHeaderFile(const string &FileName);
+ void InitEnv() const;
+
+ virtual ~mhmakefileparser()
+ {
+ SaveAutoDepsFile();
+ }
+ virtual int yylex(void);
+ virtual void yyerror(char *m);
+ virtual int yyparse()=0;
+
+ int ParseFile(const refptr<fileinfo> &FileInfo,bool SetMakeDir=false);
+
+ /* Functions to handle variables */
+ bool IsDefined(const string &Var) const;
+ bool IsEqual(const string &EqualExpr) const;
+ string ExpandExpression(const string &Expr) const;
+ string ExpandMacro(const string &Expr) const;
+ string ExpandVar(const string &Var) const;
+
+ void PrintVariables(bool Expand=false) const;
+
+ /* Functions for macro functions */
+ static funcdef m_FunctionsDef[];
+ static map<string,function_f> m_Functions;
+ static bool m_FunctionsInitialised;
+ static void InitFuncs(void);
+ string f_filter(const string &) const;
+ string f_call(const string &) const;
+ string f_if(const string &) const;
+ string f_findstring(const string &) const;
+ string f_firstword(const string &) const;
+ string f_wildcard(const string &) const;
+ string f_subst(const string &) const;
+ string f_patsubst(const string &) const;
+ string f_concat(const string &) const;
+ string f_basename(const string & Arg) const;
+ string f_notdir(const string & Arg) const;
+ string f_dir(const string & Arg) const;
+ string f_shell(const string & Arg) const;
+ string f_relpath(const string & Arg) const;
+ string f_toupper(const string & Arg) const;
+ string f_tolower(const string & Arg) const;
+ string f_exist(const string & Arg) const;
+ string f_filesindirs(const string & Arg) const;
+ string f_fullname(const string & Arg) const;
+ string f_addprefix(const string & Arg) const;
+ string f_addsuffix(const string & Arg) const;
+ string f_filterout(const string & Arg) const;
+ string f_word(const string & Arg) const;
+ string f_words(const string & Arg) const;
+
+ const refptr<fileinfo> GetFirstTarget() const
+ {
+ return m_FirstTarget;
+ }
+
+ const refptr<fileinfo> GetMakeDir() const
+ {
+ return m_MakeDir;
+ }
+
+ mh_time_t GetDate(void) const
+ {
+ return m_Date;
+ }
+
+ void UpdateDate(mh_time_t Date)
+ {
+ if (Date.IsNewer(m_Date))
+ m_Date=Date;
+ }
+
+ void AddTarget(const fileinfo* pTarget)
+ {
+ m_Targets.insert(pTarget);
+ }
+ mh_time_t BuildTarget(const refptr<fileinfo> &Target, bool bCheckTargetDir=true); /* returns the date of the target after build, especially important for phony rules, since this will be the youngest date of all dependencies */
+ void BuildDependencies(const refptr<rule> &pRule, const refptr<fileinfo> &Target, mh_time_t TargetDate, mh_time_t &YoungestDate, bool &MakeTarget);
+
+ void BuildIncludedMakefiles();
+
+ void AddIncludedMakefile(refptr<fileinfo> &MakeInfo)
+ {
+ UpdateDate(MakeInfo->GetDate());
+ m_IncludedMakefiles.push_back(MakeInfo);
+ }
+ fileinfoarray &GetIncludedMakefiles()
+ {
+ return m_IncludedMakefiles;
+ }
+
+ void IncludeAfterBuild(const string &IncludeFile)
+ {
+ m_ToBeIncludeAfterBuild.push_back(IncludeFile);
+ }
+ void ParseBuildedIncludeFiles();
+
+ void AddMakefileToMakefilesToLoad(const string &Makefile)
+ {
+ m_MakefilesToLoad.push_back(Makefile);
+ }
+ vector<string>& GetMakefilesToLoad()
+ {
+ return m_MakefilesToLoad;
+ }
+ void AddRule();
+
+ bool ExecuteCommand(string Command,string *pOutput=NULL);
+ string GetFullCommand(string Command);
+ void CreatePythonExe(const string &FullCommand);
+
+ static void InitBuildTime();
+};
+
+void SplitToItems(const string &String,vector< refptr<fileinfo> > &Items,refptr<fileinfo> Dir=curdir::GetCurDir());
+
+#endif
+
diff --git a/tools/mhmake/src/mhmakelexer.l b/tools/mhmake/src/mhmakelexer.l
new file mode 100644
index 000000000..eb27ab9ed
--- /dev/null
+++ b/tools/mhmake/src/mhmakelexer.l
@@ -0,0 +1,910 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* -------------- declaration section -------------- */
+%name mhmakelexer
+%header{
+#include "mhmakeparser.h"
+#include "fileinfo.h"
+#include "rule.h"
+#include "util.h"
+
+static uint32 LoadMakMd5(refptr<fileinfo> &Target)
+{
+ string FileName=Target->GetFullFileName();
+ FileName+=".md5_32";
+ FILE *pFile=fopen(FileName.c_str(),"rb");
+ if (!pFile)
+ return 0;
+ uint32 Md5_32=0;
+ fread(&Md5_32,sizeof(Md5_32),1,pFile);
+ fclose(pFile);
+ Target->SetCommandsMd5_32(Md5_32);
+ return Md5_32;
+}
+
+static void SaveMakMd5(refptr<fileinfo> &Target)
+{
+ string FileName=Target->GetFullFileName();
+ FileName+=".md5_32";
+ FILE *pFile=fopen(FileName.c_str(),"wb");
+ if (!pFile)
+ {
+ cout << "Error creating file "<<FileName<<endl;
+ throw(1);
+ }
+ Target->WriteMd5_32(pFile);
+ fclose(pFile);
+}
+
+%}
+
+%define LEX_PARAM TOKENVALUE &theValue
+%define MEMBERS public: \
+ struct INSTACK\
+ {\
+ YY_BUFFER_STATE m_BufferState;\
+ string m_FileName;\
+ int m_Line;\
+ INSTACK(YY_BUFFER_STATE BufferState,string FileName,int Line) :\
+ m_BufferState(BufferState),m_FileName(FileName),m_Line(Line) {}\
+ };\
+ int m_Line;\
+ int m_BraceIndent;\
+ int m_IndentSkip;\
+ iterstack<int> m_IndentStack;\
+ bool m_IgnoreIncludeError;\
+ string m_InputFileName; \
+ string m_curtoken; \
+ iterstack<INSTACK> m_IncludeStack; \
+ mhmakeparser *m_pParser;\
+ mhmakeparser *GetParser()\
+ {\
+ return m_pParser;\
+ }
+
+%define CONSTRUCTOR_INIT : m_Line(1), m_BraceIndent(0)
+
+/* -------------- rules section -------------- */
+%x INCLUDE IFDEF IF IFNDEF SKIPUNTILELSEORENDIF QUOTE MAKEEXPRES SINGLEQUOTE COMMANDPARSE
+%x IFEQ IFNEQ ERRORMACRO MESSAGEMACRO REPARSEMACRO LOAD_MAKEFILE
+
+%%
+
+ /*---------------------------------------------------------------------------*/
+[ \t\r]*\n[ ][ \t]* |
+[ \t\r]*\n {
+ PRINTF(("%s %d: NEWLINE:\n",m_InputFileName.c_str(),m_Line));
+ m_Line++;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[s\-]?include {
+ PRINTF(("%s %d: INCLUDE: ",m_InputFileName.c_str(),m_Line));
+ BEGIN(INCLUDE);
+ if (yytext[0]=='-' || yytext[0]=='s')
+ m_IgnoreIncludeError=true;
+ else
+ m_IgnoreIncludeError=false;
+ return mhmakeparser::INCLUDEMAK; // Return a newline to be sure that the previous line is completely parse by yacc (in case it is a variable definition)
+}
+
+ /*****************************************************************************/
+<INCLUDE>[ \t]* /* eat the whitespace */
+ /*---------------------------------------------------------------------------*/
+<INCLUDE>[^\r\n]+ { /* got the include file name */
+ mhmakeparser *pParser=GetParser();
+
+ string IncludeFileNames=pParser->ExpandExpression((const char*)yytext);
+ PRINTF(("%s -> %s\n",yytext,IncludeFileNames.c_str()));
+
+ const char *pTmp=IncludeFileNames.c_str();
+ while (*pTmp)
+ {
+ string IncludeFileName;
+ pTmp=NextItem(pTmp,IncludeFileName);
+ if (!IncludeFileName.empty())
+ {
+ PRINTF(("%s -> %s\n",yytext,IncludeFileName.c_str()));
+ refptr<fileinfo> pInclude=GetFileInfo(IncludeFileName,pParser->GetMakeDir());
+ /* Already build the include file, in case we already have a rule for it. */
+ if (pInclude->GetRule())
+ {
+ uint32 Md5_32=LoadMakMd5(pInclude);
+ pParser->BuildTarget(pInclude);
+ if (!pInclude->CompareMd5_32(Md5_32))
+ SaveMakMd5(pInclude);
+ }
+
+ pParser->AddIncludedMakefile(pInclude);
+
+ string strToInclude=pInclude->GetFullFileName();
+ FILE *pTmp = ::fopen(strToInclude.c_str(), "r" );
+ if ( ! pTmp )
+ {
+ if (!m_IgnoreIncludeError)
+ {
+ iterstack<INSTACK>::reverse_iterator StackIt=m_IncludeStack.rbegin();
+ while (StackIt!=m_IncludeStack.rend())
+ {
+ cout<<" in "<<StackIt->m_FileName<<" ("<<StackIt->m_Line<<")";
+ StackIt++;
+ }
+ cout<<endl;
+ cout<<"Warning error opening file "<<strToInclude<<" in "<<m_InputFileName<<" ("<<m_Line<<")\n";
+ pParser->IncludeAfterBuild(strToInclude);
+ }
+ }
+ else
+ {
+ m_IncludeStack.push(INSTACK(YY_mhmakelexer_CURRENT_BUFFER,m_InputFileName,m_Line));
+ m_Line=1;
+
+ yyin=pTmp;
+ m_InputFileName=strToInclude;
+
+ YY_mhmakelexer_SWITCH_TO_BUFFER(YY_mhmakelexer_CREATE_BUFFER( yyin, YY_BUF_SIZE ) );
+ }
+
+ }
+ }
+
+ BEGIN(INITIAL);
+}
+
+ /*---------------------------------------------------------------------------*/
+load_makefile {
+ PRINTF(("%s %d: LOAD_MAKEFILE:\n",m_InputFileName.c_str(),m_Line));
+ BEGIN(LOAD_MAKEFILE);
+ return mhmakeparser::NEWLINE; // Return a newline to be sure that the previous line is completely parse by yacc (in case it is a variable definition)
+}
+
+ /*****************************************************************************/
+<LOAD_MAKEFILE>[^\r\n]+ {
+ string ListOfMakefiles=GetParser()->ExpandExpression((const char*)yytext);
+ PRINTF(("%s %d: LOAD_MAKEFILE: '%s'\n",m_InputFileName.c_str(),m_Line,ListOfMakefiles.c_str()));
+
+ const char *pTmp=ListOfMakefiles.c_str();
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextCharItem(pTmp,Item,';');
+ if (Item.empty())
+ {
+ cerr << m_InputFileName << "("<<m_Line<<"): Error in load_makefile statement\n";
+ throw(1);
+ }
+ GetParser()->AddMakefileToMakefilesToLoad(Item);
+ }
+
+}
+ /*---------------------------------------------------------------------------*/
+<LOAD_MAKEFILE>\r?\n {
+ m_Line++;
+ BEGIN(INITIAL);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ \t]+ {
+ PRINTF(("%s %d: SPACE:\n",m_InputFileName.c_str(),m_Line));
+ return mhmakeparser::SPACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*=[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: EQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ m_Line++;
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::EQUAL;
+}
+
+[\ t]*=[ \t]* {
+ PRINTF(("%s %d: EQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::EQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*:=[ \t]*\\[ \t\r]*\n[ \t]* {
+ m_Line++;
+ PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::IMEQUAL;
+}
+
+[ \t]*:=[ \t]* {
+ PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::IMEQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*\?=[ \t]*\\[ \t\r]*\n[ \t]* {
+ m_Line++;
+ PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::OPTEQUAL;
+}
+
+[ \t]*\?=[ \t]* {
+ PRINTF(("%s %d: IMEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::OPTEQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*\+=[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: PEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ m_Line++;
+ return mhmakeparser::PEQUAL;
+}
+
+[ \t]*\+=[ \t]* {
+ PRINTF(("%s %d: PEQUAL: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::PEQUAL;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*;[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: -SEMICOLON (NEWLINE): %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ m_curtoken=g_EmptyString;
+ m_Line++;
+ BEGIN(COMMANDPARSE);
+ return mhmakeparser::NEWLINE;
+}
+
+[ \t]*;[ \t]* {
+ PRINTF(("%s %d: -SEMICOLON (NEWLINE): %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ m_curtoken=g_EmptyString;
+ BEGIN(COMMANDPARSE);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*::[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: DOUBLECOLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ m_Line++;
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOUBLECOLON;
+}
+
+[ \t]*::[ \t]* {
+ PRINTF(("%s %d: DOUBLECOLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOUBLECOLON;
+}
+
+ /*---------------------------------------------------------------------------*/
+[\ t]*:[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: COLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ m_Line++;
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::COLON;
+}
+
+[ \t]*:[ \t]* {
+ PRINTF(("%s %d: COLON: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::COLON;
+}
+
+ /*---------------------------------------------------------------------------*/
+, {
+ PRINTF(("%s %d: COMMA: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::COMMA;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*endif {
+ if (m_IndentStack.size())
+ {
+ m_IndentStack.pop();
+ PRINTF(("%s %d: %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ }
+ else
+ {
+ cerr << "Unexpected endif at line "<<m_Line<<" of "<<m_InputFileName.c_str()<<endl;
+ throw(1);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*ifdef[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFDEF);
+ m_Line++;
+ return mhmakeparser::NEWLINE;
+}
+
+^[ \t]*ifdef[ \t]+ {
+ BEGIN(IFDEF);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*if[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IF);
+ m_Line++;
+ return mhmakeparser::NEWLINE;
+}
+
+^[ \t]*if[ \t]+ {
+ BEGIN(IF);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*ifndef[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFNDEF);
+ m_Line++;
+ return mhmakeparser::NEWLINE;
+}
+
+^[ \t]*ifndef[ \t]+ {
+ BEGIN(IFNDEF);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*ifeq[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFEQ);
+ m_curtoken=g_EmptyString;
+ m_Line++;
+ return mhmakeparser::NEWLINE;
+}
+
+^[ \t]*ifeq[ \t]+ {
+ BEGIN(IFEQ);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*ifneq[ \t]*\\[ \t\r]*\n[ \t]* {
+ BEGIN(IFNEQ);
+ m_curtoken=g_EmptyString;
+ m_Line++;
+ return mhmakeparser::NEWLINE;
+}
+
+^[ \t]*ifneq[ \t]+ {
+ BEGIN(IFNEQ);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+^[ \t]*else[ \t]* {
+ if (m_IndentStack.size() && (!m_IndentStack.top()))
+ {
+ PRINTF(("%s %d: skipping else: depth %d\n",m_InputFileName.c_str(),m_Line,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ m_IndentStack.top()=1;
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+ else
+ {
+ cerr << "Unexpected else at line "<<m_Line<<" of file "<< m_InputFileName.c_str() << endl;
+ throw(1);
+ }
+}
+
+ /*****************************************************************************/
+<IFEQ>\n {
+ unput( yytext[0] );
+ m_IndentStack.push(0);
+ if (GetParser()->IsEqual(m_curtoken))
+ {
+ PRINTF(("%s %d: Not Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifeq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+<IFEQ,IFNEQ>[ \t]*\\[ \t\r]*\n[ \t]* { m_Line++; m_curtoken += g_SpaceString;}
+
+<IFEQ,IFNEQ>\r /* skip */
+
+<IFEQ,IFNEQ>[^\\\r\n]+ |
+<IFEQ,IFNEQ>\\ { m_curtoken += (const char *)yytext; }
+
+ /*****************************************************************************/
+<IFNEQ>\n {
+ unput( yytext[0] );
+ m_IndentStack.push(0);
+ if (!GetParser()->IsEqual(m_curtoken))
+ {
+ PRINTF(("%s %d: Not Skipping ifneq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifneq %s: depth %d\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str(),m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<IF,IFDEF,IFNDEF>[ \t\r]* /* skip */
+
+ /*---------------------------------------------------------------------------*/
+<IF>[a-zA-Z0-9_]+ {
+ m_IndentStack.push(0);
+ string Val=GetParser()->ExpandVar((const char *)yytext);
+ if (Val.empty() || Val=="0")
+ {
+ PRINTF(("%s %d: Skipping if %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+ else
+ {
+ PRINTF(("%s %d: Not Skipping if %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<IFDEF>[a-zA-Z0-9_]+ {
+ m_IndentStack.push(0);
+ if (GetParser()->IsDefined((const char *)yytext))
+ {
+ PRINTF(("%s %d: Not Skipping ifdef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifdef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<IFNDEF>[a-zA-Z0-9_]+ {
+ m_IndentStack.push(0);
+ if (!GetParser()->IsDefined((const char *)yytext)) {
+ PRINTF(("%s %d: Not Skipping ifndef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ PRINTF(("%s %d: Skipping ifndef %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext,m_IndentStack.size()));
+ m_IndentSkip=m_IndentStack.size();
+ BEGIN(SKIPUNTILELSEORENDIF);
+ }
+}
+
+ /*****************************************************************************/
+<SKIPUNTILELSEORENDIF>\n[ ]*endif {
+ m_Line++;
+ if (!m_IndentStack.size())
+ {
+ cerr << "Unexpected endif at line "<<m_Line<<" of "<<m_InputFileName.c_str()<<endl;
+ throw(1);
+ }
+ else
+ {
+ m_IndentStack.pop();
+ PRINTF(("%s %d: endif: depth %d\n",m_InputFileName.c_str(),m_Line,m_IndentStack.size()));
+ if (m_IndentStack.size()==m_IndentSkip-1) BEGIN(INITIAL);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n[ ]*else {
+ m_Line++;
+ PRINTF(("%s %d: else: depth %d\n",m_InputFileName.c_str(),m_Line,m_IndentStack.size()));
+ if (m_IndentStack.top())
+ {
+ cerr << "Unexpected else at line "<<m_Line<<" of file "<< m_InputFileName.c_str() << endl;
+ throw(1);
+ }
+ m_IndentStack.top()=1;
+ if (m_IndentStack.size()==m_IndentSkip)
+ {
+ BEGIN(INITIAL);
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n[ ]*if(def|ndef|eq|neq) {
+ m_Line++;
+ m_IndentStack.push(0);
+ PRINTF(("%s %d: %s: depth %d\n",m_InputFileName.c_str(),m_Line,yytext+1,m_IndentStack.size()));
+}
+
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>[a-zA-Z]+ /* skip */
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>[^a-zA-Z\n]+ /* skip */
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n[ ]*[a-zA-Z]+ m_Line++;
+ /*---------------------------------------------------------------------------*/
+<SKIPUNTILELSEORENDIF>\n {
+ m_Line++;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*#[^\n]* {
+ PRINTF(("%s %d: -COMMENT: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t]*\\[ \t\r]*\n[ \t]* {
+ PRINTF(("%s %d: SPACE:\n",m_InputFileName.c_str(),m_Line));
+ m_Line++;
+ return mhmakeparser::SPACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\.PHONY {
+ PRINTF(("%s %d: .PHONY: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::PHONY;
+}
+
+ /*---------------------------------------------------------------------------*/
+export {
+ PRINTF(("%s %d: export: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::EXPORT;
+}
+
+
+ /*---------------------------------------------------------------------------*/
+[a-zA-Z]:[a-zA-Z0-9\\\._\~\-%\@<&/]+\\[ \t\r]*\n {
+ int EndIndex=::strlen((const char*)yytext);
+ while (strchr(" \t\r\n\\",yytext[--EndIndex]));
+ yyless(EndIndex+1);
+
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+[a-zA-Z]:[a-zA-Z0-9\\\._\~\-%\@<&/]+ {
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*]|\\\ |\\#)+\\[ \t\r]*\n {
+ int EndIndex=::strlen((const char*)yytext);
+ while (strchr(" \t\r\n\\",yytext[--EndIndex]));
+ yyless(EndIndex+1);
+
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*]|\\\ |\\#)+\+= {
+ int Len;
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ Len=strlen((const char *)yytext)-2;
+ yyless(Len);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+([a-zA-Z0-9\\\._\~\-\+%\@<&;/\*]|\\\ |\\#)+ {
+ PRINTF(("%s %d: STRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+\" {
+ BEGIN(QUOTE);
+ yymore();
+}
+
+ /*---------------------------------------------------------------------------*/
+\' {
+ BEGIN(SINGLEQUOTE);
+ yymore();
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\( {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ BEGIN(MAKEEXPRES);
+ yymore();
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\([ \t]*error[ \t]+ {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN ERROR MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ BEGIN(ERRORMACRO);
+ m_curtoken=g_EmptyString;
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\([ \t]*message[ \t]+ {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN MESSAGE MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ BEGIN(MESSAGEMACRO);
+ m_curtoken=g_EmptyString;
+}
+
+ /*---------------------------------------------------------------------------*/
+\$\([ \t]*reparse[ \t]+ {
+ m_BraceIndent++;
+ PRINTF(("%s %d: BEGIN REPARSE MACRO $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ BEGIN(REPARSEMACRO);
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\( {
+ PRINTF(("%s %d: OPENBRACE: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::OPENBRACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\) {
+ PRINTF(("%s %d: CLOSEBRACE: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ return mhmakeparser::CLOSEBRACE;
+}
+
+ /*---------------------------------------------------------------------------*/
+\$[<@/] {
+ PRINTF(("%s %d: DOLLAREXPR: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOLLAREXPR;
+}
+
+ /*---------------------------------------------------------------------------*/
+[ \t\r]*\n\t[ \t]* {
+ /* token newline */
+ PRINTF(("%s %d: NEWLINE\n",m_InputFileName.c_str(),m_Line));
+ m_Line++;
+ m_curtoken=g_EmptyString;
+ BEGIN(COMMANDPARSE);
+ return mhmakeparser::NEWLINE;
+}
+
+ /*---------------------------------------------------------------------------*/
+[^\n] {
+ PRINTF(("%s %d: ANYCHAR: %d: %s\n",m_InputFileName.c_str(),m_Line,m_Line,yytext));
+}
+
+ /*****************************************************************************/
+
+<COMMANDPARSE>[ \t\r]*\n {
+ PRINTF(("%s %d: COMMAND: %d: %s\n",m_InputFileName.c_str(),m_Line,m_Line,m_curtoken.c_str()));
+ theValue.theString=m_curtoken;
+ m_Line++;
+ BEGIN(INITIAL);
+ return mhmakeparser::COMMAND;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t\r]*\n\t[ \t]* {
+ PRINTF(("%s %d: COMMAND: %s\n",m_InputFileName.c_str(),m_Line,m_curtoken.c_str()));
+ theValue.theString=m_curtoken;
+ m_Line++;
+ m_curtoken=g_EmptyString;
+ return mhmakeparser::COMMAND;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t]*\\[ \t\r]*\n[ \t]* {
+ m_Line++;
+ m_curtoken+=g_SpaceString;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ ]+ {
+ m_curtoken+=g_SpaceString;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[^ \r\n#\\]+ |
+<COMMANDPARSE>\\ {
+ m_curtoken+=(const char *)yytext;
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t]*\\#[^\n]* {
+ char ToAdd[100];
+ int i=0;
+ int nChars=(strchr((const char *)yytext,'#')-(char*)yytext)+1;
+ while (strchr(" \t",yytext[i]))
+ {
+ ToAdd[i]=yytext[i];
+ i++;
+ }
+ ToAdd[i++]='#';
+ ToAdd[i++]=0;
+ m_curtoken+=ToAdd;
+ yyless(nChars);
+}
+
+ /*---------------------------------------------------------------------------*/
+<COMMANDPARSE>[ \t]*#[^\n]* {
+ PRINTF(("%s %d: -COMMENT: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+}
+
+ /*****************************************************************************/
+<QUOTE>\" {
+ PRINTF(("%s %d: QUOTEDSTRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ BEGIN(INITIAL);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+<QUOTE>\r /* skip */
+
+<QUOTE>[^\\\"\r\n]+ |
+<QUOTE>\\ |
+<QUOTE>\\\" |
+<QUOTE>\\# {
+ yymore();
+}
+
+ /*****************************************************************************/
+<SINGLEQUOTE>\' {
+ PRINTF(("%s %d: QUOTEDSTRING: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ BEGIN(INITIAL);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::STRING;
+}
+
+ /*---------------------------------------------------------------------------*/
+<SINGLEQUOTE>\r /* skip */
+
+<SINGLEQUOTE>[^\\\'\r\n]+ |
+<SINGLEQUOTE>\\ |
+<SINGLEQUOTE>\\\' |
+<SINGLEQUOTE>\\# {
+ yymore();
+}
+
+ /*****************************************************************************/
+<ERRORMACRO>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE ERROR MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: ERRORMACRO: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ yyless(0);
+ cerr<<GetParser()->ExpandExpression((const char*)yytext)<<endl;
+ throw(1);
+ } else {
+ yymore();
+ }
+}
+
+ /*****************************************************************************/
+<MESSAGEMACRO>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE MESSAGE MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: MESSAGEMACRO: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ yytext[strlen((const char*)yytext)-1]=0;
+ cerr<<GetParser()->ExpandExpression((const char*)yytext)<<endl;
+ BEGIN(INITIAL);
+ } else {
+ yymore();
+ }
+}
+
+ /*****************************************************************************/
+<REPARSEMACRO>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE REPARSE MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: REPARSEMACRO: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ string Deps=GetParser()->ExpandExpression((const char*)yytext);
+ PRINTF(("%s %d: REPARSEMACRO expanded: %s\n",m_InputFileName.c_str(),m_Line,Deps.c_str()));
+ string::const_reverse_iterator It=Deps.rbegin()+1; // +1 because we don't want the latest brace
+ string::const_reverse_iterator ItBeg=Deps.rend();
+ while (It!= ItBeg)
+ {
+ char Char=*It++;
+ if (Char==';') Char='\n';
+ unput(Char);
+ }
+ BEGIN(INITIAL);
+ }
+ else
+ {
+ yymore();
+ }
+}
+
+ /*****************************************************************************/
+<MAKEEXPRES>\) {
+ m_BraceIndent--;
+ PRINTF(("%s %d: CLOSE BRACE MAKEEXPRES MACRO ): %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ if (!m_BraceIndent)
+ {
+ PRINTF(("%s %d: DOLLAREXPR: %s\n",m_InputFileName.c_str(),m_Line,yytext));
+ BEGIN(INITIAL);
+ theValue.theString=(const char *)yytext;
+ return mhmakeparser::DOLLAREXPR;
+ }
+ else
+ {
+ yymore();
+ }
+}
+
+ /*---------------------------------------------------------------------------*/
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\$\( {
+ m_BraceIndent++;
+ PRINTF(("%s %d: MACRO extra $(: %d\n",m_InputFileName.c_str(),m_Line,m_BraceIndent));
+ yymore();
+}
+
+ /*---------------------------------------------------------------------------*/
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>[^$\(\)\r\n]+ |
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\$ |
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\( {
+ yymore();
+}
+<MAKEEXPRES,ERRORMACRO,MESSAGEMACRO,REPARSEMACRO>\r?\n {
+ m_Line++;
+}
+
+<SKIPUNTILELSEORENDIF><<EOF>> {
+ cerr << "Missing endif or else statement. #else or #endif used?\n";
+ throw(1);
+}
+
+<<EOF>> {
+ if (m_BraceIndent)
+ {
+ cerr << "Missing closing ) of macro usage in "<<m_InputFileName.c_str()<<".\n";
+ throw(1);
+ }
+ if (!m_IncludeStack.size())
+ {
+ if (m_IndentStack.size())
+ {
+ cerr << "Missing endif or else statement in "<<m_InputFileName.c_str()<<". #else or #endif used?\n";
+ throw(1);
+ }
+ yyterminate();
+ }
+ else
+ {
+ ::fclose(YY_mhmakelexer_CURRENT_BUFFER->yy_input_file);
+ YY_mhmakelexer_DELETE_BUFFER(YY_mhmakelexer_CURRENT_BUFFER);
+ INSTACK *pInStack=&m_IncludeStack.top();
+ YY_mhmakelexer_SWITCH_TO_BUFFER(pInStack->m_BufferState);
+ m_InputFileName=pInStack->m_FileName;
+ m_Line=pInStack->m_Line;
+ m_IncludeStack.pop();
+ }
+}
+%%
+
diff --git a/tools/mhmake/src/mhmakeparser.y b/tools/mhmake/src/mhmakeparser.y
new file mode 100644
index 000000000..0a8985209
--- /dev/null
+++ b/tools/mhmake/src/mhmakeparser.y
@@ -0,0 +1,224 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+/* -------------- declaration section -------------- */
+
+%name mhmakeparser
+
+%define LLOC m_yyloc
+%define LTYPE int // This is the type of LLOC (defined in mhmakefileparser)
+
+%define LVAL m_theTokenValue
+%define STYPE TOKENVALUE // This is the type of LVAL (defined in mhmakefileparser)
+
+%define INHERIT : public mhmakefileparser
+
+%define CONSTRUCTOR_PARAM const map<string,string> &CommandLineVariables
+%define CONSTRUCTOR_INIT : mhmakefileparser(CommandLineVariables)
+
+%header{
+#include "mhmakefileparser.h"
+#include "rule.h"
+#include "util.h"
+%}
+
+%token <theString> COMMAND
+%token <theString> COMMA OPENBRACE CLOSEBRACE
+%token <theString> STRING DOLLAREXPR EQUAL COLON DOUBLECOLON
+%token IMEQUAL PEQUAL OPTEQUAL PHONY EXPORT NEWLINE INCLUDEMAK SPACE
+
+%type <theString> expression nonspaceexpression simpleexpression
+%type <theString> maybeemptyexpression
+%type <theString> expression_nocolorequal simpleexpression_nocolorequal nonspaceexpression_nocolorequal
+%type <ival> rulecolon
+
+%start file
+
+/* -------------- rules section -------------- */
+/* Sample parser. Does count Chars in a line, and lines in file */
+%%
+file : statements
+ {
+ if (m_pCurrentItems)
+ {
+ PRINTF(("Adding rule : %s\n",(*m_pCurrentItems)[0]->GetFullFileName().c_str()));
+ AddRule();
+ }
+ }
+;
+
+statements :
+ | statements statement
+;
+
+statement: NEWLINE |
+ includemak |
+ ruledef |
+ phonyrule |
+ varassignment |
+ imvarassignment |
+ pvarassignment |
+ optvarassignment |
+ exportrule |
+ COMMAND
+ {
+ if (!m_pCurrentRule)
+ {
+ m_pCurrentRule=refptr<rule>(new rule(this));
+ }
+ m_pCurrentRule->AddCommand($1);
+ PRINTF(("Adding command : %s\n",$1.c_str()));
+ }
+;
+
+includemak:
+ {
+ if (m_pCurrentItems)
+ {
+ PRINTF(("Adding rule : %s\n",(*m_pCurrentItems)[0]->GetFullFileName().c_str()));
+ AddRule();
+ }
+ } INCLUDEMAK
+;
+
+ruledef: expression_nocolorequal rulecolon maybeemptyexpression
+ {
+ if (m_pCurrentItems)
+ {
+ PRINTF(("Adding rule : %s\n",(*m_pCurrentItems)[0]->GetFullFileName().c_str()));
+ AddRule();
+ }
+
+ m_pCurrentItems=new fileinfoarray;
+ m_pCurrentDeps=new fileinfoarray;
+ #ifdef _DEBUG
+ if (!ExpandExpression($1).size())
+ {
+ printf("Empty left hand side in rule: %s : %s\n",$1.c_str(),$3.c_str());
+ throw(1);
+ }
+ #endif
+ SplitToItems(ExpandExpression($1),*m_pCurrentItems,m_MakeDir);
+ SplitToItems(ExpandExpression($3),*m_pCurrentDeps,m_MakeDir);
+ m_DoubleColonRule= ($2==1) ;
+ PRINTF(("Defining rule %s : %s\n",$1.c_str(),$3.c_str()));
+ PRINTF((" Expanded to %s : %s\n",ExpandExpression($1).c_str(),ExpandExpression($3).c_str()));
+ }
+;
+
+rulecolon: COLON {$$=0;} |
+ DOUBLECOLON {$$=1;}
+;
+
+phonyrule: PHONY COLON expression
+ {
+ vector< refptr<fileinfo> > Items;
+ SplitToItems(ExpandExpression($3),Items,m_MakeDir);
+ vector< refptr<fileinfo> >::iterator pIt=Items.begin();
+ while (pIt!=Items.end())
+ {
+ (*pIt)->SetPhony();
+ pIt++;
+ }
+ PRINTF(("Defining phony rule : %s\n",$3.c_str()));
+ PRINTF((" Expanded to : %s\n",ExpandExpression($3).c_str()));
+ }
+ NEWLINE
+;
+
+exportrule: EXPORT SPACE exportstrings NEWLINE
+;
+
+exportstrings : exportstring |
+ exportstring SPACE exportstrings
+;
+
+exportstring : STRING
+ {
+ m_Exports.push_back($1+"="+ExpandExpression(ExpandVar($1)));
+ PRINTF(("Exporting %s : %s\n",$1.c_str(),ExpandExpression(ExpandVar($1)).c_str()));
+ }
+;
+
+varassignment: STRING EQUAL maybeemptyexpression
+ {
+ m_Variables[$1]=$3;
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), $3.c_str()));
+ }
+;
+
+imvarassignment: STRING IMEQUAL maybeemptyexpression
+ {
+ m_Variables[$1]=ExpandExpression($3);
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), m_Variables[$1].c_str()));
+ }
+;
+
+pvarassignment: STRING PEQUAL maybeemptyexpression
+ {
+ m_Variables[$1]=m_Variables[$1]+g_SpaceString+$3;
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), $3.c_str()));
+ }
+;
+
+optvarassignment: STRING OPTEQUAL maybeemptyexpression
+ {
+ if (!IsDefined($1))
+ {
+ m_Variables[$1]=$3;
+ PRINTF(("Setting variable %s to %s\n",$1.c_str(), $3.c_str()));
+ }
+ }
+;
+
+maybeemptyexpression: NEWLINE {$$=g_EmptyString;} |
+ expression NEWLINE
+;
+
+expression: nonspaceexpression |
+ expression SPACE nonspaceexpression {$$=$1+g_SpaceString+$3;}
+;
+
+expression_nocolorequal: nonspaceexpression_nocolorequal |
+ expression_nocolorequal SPACE nonspaceexpression_nocolorequal {$$=$1+g_SpaceString+$3;}
+;
+
+nonspaceexpression: simpleexpression |
+ nonspaceexpression simpleexpression {$$=$1+$2;}
+;
+
+nonspaceexpression_nocolorequal: simpleexpression_nocolorequal |
+ nonspaceexpression_nocolorequal simpleexpression_nocolorequal {$$=$1+$2;}
+;
+
+simpleexpression: simpleexpression_nocolorequal |
+ EQUAL |
+ COLON |
+ DOUBLECOLON |
+ COMMA
+;
+
+simpleexpression_nocolorequal: STRING {$$=$1;} |
+ DOLLAREXPR {$$=$1;}
+;
+
+%%
+/* -------------- body section -------------- */
+
diff --git a/tools/mhmake/src/refptr.h b/tools/mhmake/src/refptr.h
new file mode 100644
index 000000000..38b62b4ca
--- /dev/null
+++ b/tools/mhmake/src/refptr.h
@@ -0,0 +1,124 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __REFPTR_H__
+#define __REFPTR_H__
+
+#ifdef _DEBUG
+#define PRINTF(arg) if (g_PrintLexYacc) printf arg
+#else
+#define PRINTF(arg)
+#endif
+
+template <class T> class iterstack: public stack<T>
+{
+ public:
+ typedef typename deque<T>::iterator iterator;
+ typedef typename deque<T>::reverse_iterator reverse_iterator;
+
+ iterator begin()
+ {
+ return stack<T>::c.begin();
+ }
+ iterator end()
+ {
+ return stack<T>::c.end();
+ }
+ reverse_iterator rbegin()
+ {
+ return stack<T>::c.rbegin();
+ }
+ reverse_iterator rend()
+ {
+ return stack<T>::c.rend();
+ }
+};
+
+struct refbase
+{
+ int m_Count;
+ refbase()
+ {
+ m_Count=1;
+ }
+};
+
+// Template class T needs to be derived from refbase;
+template <class T> class refptr
+{
+ T *m_RefPtr;
+public:
+ refptr()
+ {
+ m_RefPtr=NULL;
+ }
+ refptr(T *Ptr) {
+ m_RefPtr=Ptr;
+ }
+ refptr(const refptr<T> &RefPtr)
+ {
+ m_RefPtr=RefPtr.m_RefPtr;
+ if (m_RefPtr)
+ m_RefPtr->m_Count++;
+ }
+ ~refptr()
+ {
+ if (m_RefPtr && !--m_RefPtr->m_Count)
+ {
+ delete m_RefPtr;
+ }
+ }
+ refptr<T> &operator=(const refptr<T>& Src)
+ {
+ if (Src.m_RefPtr!=m_RefPtr)
+ {
+ this->~refptr();
+ new (this) refptr<T>(Src);
+ }
+ return *this;
+ }
+
+ refptr<T> &operator=(T *pPtr) // Assumes that T has reference count 1 and has no other users
+ {
+ #if defined(_DEBUG) && defined(_MSC_VER)
+ if (pPtr && pPtr->m_Count!=1)
+ DebugBreak();
+ #endif
+ this->~refptr();
+ m_RefPtr=pPtr;
+ return *this;
+ }
+
+ T & operator*() const
+ {
+ return *m_RefPtr;
+ }
+ T * operator->() const
+ {
+ return m_RefPtr;
+ }
+ operator T*() const
+ {
+ return m_RefPtr;
+ }
+};
+
+#endif
+
diff --git a/tools/mhmake/src/rule.cpp b/tools/mhmake/src/rule.cpp
new file mode 100644
index 000000000..e8bc1b6d6
--- /dev/null
+++ b/tools/mhmake/src/rule.cpp
@@ -0,0 +1,242 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "fileinfo.h"
+#include "rule.h"
+#include "util.h"
+#include "mhmakeparser.h"
+
+refptr<rule> NullRule;
+
+map< string, vector<pair<string,refptr<rule> > > > IMPLICITRULE::m_ImplicitRules;
+
+makecommand g_MakeCommand; // Order is important since sm_Statics is using g_MakeCommand
+loadedmakefile::loadedmakefile_statics loadedmakefile::sm_Statics;
+
+///////////////////////////////////////////////////////////////////////////////
+static bool FindDep(const string &DepName,vector<pair<string,refptr<rule> > >&ImplicitRule,refptr<rule> &Rule)
+{
+ vector<pair<string,refptr<rule> > >::iterator SecIt=ImplicitRule.begin();
+ while (SecIt!=ImplicitRule.end())
+ {
+ if (SecIt->first==DepName)
+ {
+ #ifdef _DEBUG
+ // Check if the rule has the same commands
+ vector<string> &OldCommands=SecIt->second->GetCommands();
+ vector<string> &NewCommands=Rule->GetCommands();
+
+ bool bCommandsDifferent=OldCommands.size()!=NewCommands.size();
+ if (g_PrintMultipleDefinedRules || bCommandsDifferent)
+ {
+ if (bCommandsDifferent)
+ cerr << "Implicit Rule '"<< DepName << "' defined twice with different commands\n";
+ else
+ cerr << "Implicit Rule '"<< DepName << "' defined twice with same commands\n";
+ cerr << "Command 1: makedir = " << SecIt->second->GetMakefile()->GetMakeDir()->GetFullFileName()<< endl;
+
+ vector<string>::const_iterator It;
+ if (bCommandsDifferent)
+ {
+ It=OldCommands.begin();
+ while (It!=OldCommands.end())
+ {
+ cerr << " " << *It << endl;
+ }
+ }
+ cerr << "Command 2: makedir = "<< Rule->GetMakefile()->GetMakeDir()->GetFullFileName()<< endl;
+ if (bCommandsDifferent)
+ {
+ It=NewCommands.begin();
+ while (It!=NewCommands.end())
+ {
+ cerr << " " << *It << endl;
+ }
+ throw(1);
+ }
+ }
+ mhmakeparser *pOldMakefile=SecIt->second->GetMakefile();
+ mhmakeparser *pNewMakefile=Rule->GetMakefile();
+ vector<string>::iterator OldIt=OldCommands.begin();
+ vector<string>::iterator NewIt=NewCommands.begin();
+ while (OldIt!=OldCommands.end())
+ {
+ if (pOldMakefile->ExpandExpression(*OldIt)!=pNewMakefile->ExpandExpression(*NewIt))
+ {
+ cerr << "Implicit Rule '"<< DepName << "' defined twice with different commands\n";
+ cerr << "Command 1: makedir = " << pOldMakefile->GetMakeDir()->GetFullFileName()<< endl;
+ cerr << " " << pOldMakefile->ExpandExpression(*OldIt) << endl;
+ cerr << "Command 2: makedir = "<< pNewMakefile->GetMakeDir()->GetFullFileName()<< endl;
+ cerr << " " << pNewMakefile->ExpandExpression(*NewIt) << endl;
+ throw(1);
+ }
+ OldIt++;
+ NewIt++;
+ }
+ #endif
+ return true;
+ }
+ SecIt++;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void IMPLICITRULE::AddImplicitRule(const refptr<fileinfo> &Target,const vector< refptr<fileinfo> > &Deps,refptr<rule> Rule)
+{
+ vector<pair<string,refptr<rule> > >& ImplicitRule=m_ImplicitRules[Target->GetFullFileName()];
+ if (Deps.size())
+ {
+ vector< refptr<fileinfo> >::const_iterator DepIt=Deps.begin();
+ while (DepIt!=Deps.end())
+ {
+ const string &DepName=(*DepIt)->GetFullFileName();
+ if (!FindDep(DepName,ImplicitRule,Rule))
+ ImplicitRule.push_back(pair<string,refptr<rule> >(DepName,Rule));
+ DepIt++;
+ }
+ }
+ else
+ {
+ if (!FindDep(g_EmptyString,ImplicitRule,Rule))
+ ImplicitRule.push_back(pair<string,refptr<rule> >(g_EmptyString,Rule));
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void IMPLICITRULE::SearchImplicitRule(const refptr<fileinfo> &Target,vector< pair<refptr<fileinfo>,refptr<rule> > >&Result)
+{
+ string TargetFileName=Target->GetFullFileName();
+
+ map< string, vector<pair<string,refptr<rule> > > >::iterator ImpRegExIt=m_ImplicitRules.begin();
+ while (ImpRegExIt!=m_ImplicitRules.end())
+ {
+ matchres Res;
+
+ if (PercentMatch(TargetFileName,ImpRegExIt->first,&Res))
+ {
+ vector<pair<string,refptr<rule> > >::iterator ResIt=ImpRegExIt->second.begin();
+ while (ResIt!=ImpRegExIt->second.end())
+ {
+#ifdef _DEBUG
+ if (!ResIt->second)
+ {
+ cerr << "No rhs for implicit rule : "<< ImpRegExIt->first << endl;
+ throw(1);
+ }
+#endif
+ ResIt->second->SetStem(Res.m_Stem);
+ if (!ResIt->first.empty())
+ {
+ string Dependent=ReplaceWithStem(ResIt->first,Res.m_Stem);
+ Result.push_back(pair<refptr<fileinfo>,refptr<rule> >(GetFileInfo(Dependent),ResIt->second));
+ }
+ else
+ Result.push_back(pair<refptr<fileinfo>,refptr<rule> >(NullFileInfo,ResIt->second));
+ ResIt++;
+ }
+ }
+ ImpRegExIt++;
+ }
+ return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool rule::operator != (const rule &Other)
+{
+ if (m_Commands.size()!=Other.m_Commands.size())
+ return true;
+
+ vector<string>::const_iterator It=m_Commands.begin();
+ vector<string>::const_iterator OtherIt=Other.m_Commands.begin();
+ while (It!=m_Commands.end())
+ {
+ if (m_pMakefile->ExpandExpression(*It)!=Other.m_pMakefile->ExpandExpression(*OtherIt))
+ return true;
+ It++;
+ OtherIt++;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void rule::SetTargetsIsBuild(uint32 Md5_32)
+{
+ vector< fileinfo* >::iterator It=m_Targets.begin();
+ while (It!=m_Targets.end())
+ {
+ (*It)->SetCommandsMd5_32(Md5_32);
+ (*It)->SetBuild();
+ m_pMakefile->AddTarget(*It);
+ It++;
+ }
+}
+
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void IMPLICITRULE::PrintImplicitRules()
+{
+ map< string, vector<pair<string,refptr<rule> > > >::iterator ImpRegExIt=m_ImplicitRules.begin();
+ while (ImpRegExIt!=m_ImplicitRules.end())
+ {
+ vector<pair<string,refptr<rule> > >::iterator SecIt=ImpRegExIt->second.begin();
+ cout << ImpRegExIt->first << " :\n";
+ while (SecIt!=ImpRegExIt->second.end())
+ {
+ cout << " : " << SecIt->first <<endl;
+ if (SecIt->second)
+ {
+ SecIt->second->PrintCommands();
+ }
+ else
+ {
+ cout << " No rhs\n";
+ }
+ SecIt++;
+ }
+ ImpRegExIt++;
+ }
+ return;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void rule::PrintCommands(refptr<fileinfo> Target) const
+{
+ if (Target)
+ m_pMakefile->SetRuleThatIsBuild(Target);
+
+ vector<string>::const_iterator pCommandIt=m_Commands.begin();
+ while (pCommandIt!=m_Commands.end())
+ {
+ cout<<g_SpaceString<<*pCommandIt<<endl;
+ if (Target)
+ {
+ cout<<" ("<<m_pMakefile->ExpandExpression(*pCommandIt)<<")\n";
+ }
+ pCommandIt++;
+ }
+}
+
+#endif
+
+
diff --git a/tools/mhmake/src/rule.h b/tools/mhmake/src/rule.h
new file mode 100644
index 000000000..c8235a8bc
--- /dev/null
+++ b/tools/mhmake/src/rule.h
@@ -0,0 +1,89 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __RULE_H__
+#define __RULE_H__
+
+#include "refptr.h"
+#include "md5.h"
+class mhmakeparser;
+class fileinfo;
+extern refptr<fileinfo> NullFileInfo;
+
+class rule: public refbase
+{
+ vector<string> m_Commands;
+ string m_Stem; // Contains the stem in case the rule is part of an implicit rule (filled in in the implicit search)
+ mhmakeparser* m_pMakefile;
+ vector< fileinfo* > m_Targets; /* Targets that are build with this rule, do not use refptr here because otherwise we get circular references */
+public:
+ rule(mhmakeparser *pMakefile): m_pMakefile(pMakefile)
+ {
+ }
+
+ void AddCommand(const string &Command)
+ {
+ if (!Command.empty())
+ m_Commands.push_back(Command);
+ }
+ vector<string>& GetCommands()
+ {
+ return m_Commands;
+ }
+ void PrintCommands(refptr<fileinfo> Target=NullFileInfo) const;
+
+ void SetStem(const string &Stem)
+ {
+ m_Stem=Stem;
+ }
+ const string &GetStem() const
+ {
+ return m_Stem;
+ }
+ void SetMakefile(mhmakeparser *pMakefile)
+ {
+ m_pMakefile=pMakefile;
+ }
+ mhmakeparser *GetMakefile()
+ {
+ return m_pMakefile;
+ }
+ bool operator != (const rule &Rule);
+
+ void AddTarget(fileinfo *pTarget)
+ {
+ m_Targets.push_back(pTarget);
+ }
+ void SetTargetsIsBuild(uint32 Md5_32);
+};
+
+class IMPLICITRULE
+{
+ static map< string, vector<pair<string,refptr<rule> > > > m_ImplicitRules;
+public:
+ static void AddImplicitRule(const refptr<fileinfo> &Target,const vector< refptr<fileinfo> >&Deps,refptr<rule> pRule);
+ static void SearchImplicitRule(const refptr<fileinfo> &Target,vector< pair<refptr<fileinfo>,refptr<rule> > >&Result);
+ static void PrintImplicitRules();
+};
+
+extern refptr<rule> NullRule;
+
+#endif
+
diff --git a/tools/mhmake/src/stdafx.cpp b/tools/mhmake/src/stdafx.cpp
new file mode 100644
index 000000000..feb841fea
--- /dev/null
+++ b/tools/mhmake/src/stdafx.cpp
@@ -0,0 +1,24 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+// This file is only used to generate the pre-compiled header files. The stdafx.h file
+// should only contain includes of headers that will not be likely to change (like system header files)
+
+#include "stdafx.h"
diff --git a/tools/mhmake/src/stdafx.h b/tools/mhmake/src/stdafx.h
new file mode 100644
index 000000000..d7e7bf4b7
--- /dev/null
+++ b/tools/mhmake/src/stdafx.h
@@ -0,0 +1,83 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently, but
+// are changed infrequently
+//
+
+#if !defined(AFX_STDAFX_H__CDC9F92E_2B83_4EFC_92B5_44861521ED45__INCLUDED_)
+#define AFX_STDAFX_H__CDC9F92E_2B83_4EFC_92B5_44861521ED45__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#pragma warning (disable:4786)
+#pragma warning (disable:4503)
+#pragma warning (disable:4530)
+#endif // _MSC_VER > 1000
+
+#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
+
+#include <iostream>
+#include <string>
+#include <vector>
+#include <map>
+#include <set>
+#include <stack>
+#include <algorithm>
+
+#ifdef _MSC_VER
+#include <io.h>
+#define __CDECL __cdecl
+#else
+#define __CDECL
+#define _stricmp strcasecmp
+#define _strnicmp strncasecmp
+#define MAX_PATH 255
+#include <sys/wait.h>
+#include <popt.h>
+#include <glob.h>
+#endif
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <malloc.h>
+#ifdef _MSC_VER
+#include <crtdbg.h>
+#endif
+#include <string.h>
+#ifdef _MSC_VER
+#include <direct.h>
+#define mkdir(name,mode) _mkdir(name)
+#endif
+#include <time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+using namespace std;
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__CDC9F92E_2B83_4EFC_92B5_44861521ED45__INCLUDED_)
+
diff --git a/tools/mhmake/src/util.cpp b/tools/mhmake/src/util.cpp
new file mode 100644
index 000000000..2e0b7dea6
--- /dev/null
+++ b/tools/mhmake/src/util.cpp
@@ -0,0 +1,708 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#include "stdafx.h"
+
+#include "rule.h"
+#include "util.h"
+#include "mhmakeparser.h"
+
+static char s_UsageString[]=
+"\
+Usage: mhmake [-f <Makefile>] [-[c|C] <RunDir>] [<Var>=<Value>]\n\
+ [-a] [-q] [-s] [-v] [targets]+\n"
+#ifdef _DEBUG
+"\
+ [-p] [-n] [-e] [-l] [-w] [-d] [-CD] [-m] [-b]\n"
+#endif
+"\n\
+ <Makefile> : Makefile to load (if not specified 'makefile' is used\n\
+ <RunDir> : Make is setting the current directory to this directory at the\n\
+ start.\n\
+ <Var> : Defines a variable\n\
+ <Value> : Value of the variable\n\
+ -a : Rebuild all targets\n\
+ -s : Rescan automatic dependencies\n\
+ -v : Print version information\n\
+ -q : Quiet. Disable all output \n"
+#ifdef _DEBUG
+"\n\
+ The following options are additional options in mhmake_dbg which are not\n\
+ available in mhmake. These are mainly options for debugging purposes.\n\
+ -e : Dump Vars and Rules on error\n\
+ -w : Print additional information\n\
+ -p : Prints the variables and the rules before building\n\
+ -n : Only prints the commands, but does not execute them\n\
+ -l : Print parser debug information\n\
+ -d : Print the dependency checking\n\
+ -CD : Do circular dependency checking (targets depending on itself)\n\
+ -m : Create md5 database in md5.database in start directory. \n\
+ -b : Print build tree. \n\
+ -D : Print all double defined rules (even if commands are the same) \n\
+"
+#else
+"\
+\n\
+It is adviced during creation of makefiles to use mhmake_dbg. It has additional\n\
+debugging options and does some extra error checking.\n\
+For a description of the additional options: run mhmake_dbg -h\n\
+"
+#endif
+;
+
+#ifdef _DEBUG
+bool g_PrintVarsAndRules=false;
+bool g_DoNotExecute=false;
+bool g_BuildMd5Db=false;
+bool g_GenProjectTree=false;
+bool g_DumpOnError=false;
+bool g_PrintAdditionalInfo=false;
+bool g_pPrintDependencyCheck=false;
+bool g_CheckCircularDeps=false;
+bool g_PrintLexYacc=false;
+bool g_PrintMultipleDefinedRules=false;
+#endif
+
+bool g_Quiet=false;
+bool g_RebuildAll=false;
+bool g_ForceAutoDepRescan=false;
+
+const string g_EmptyString;
+const string g_SpaceString(" ");
+
+///////////////////////////////////////////////////////////////////////////////
+void PrintVersionInfo(void)
+{
+ static const char VersionStr[]="\
+mhmake : GNU compatible make tool with extensions\n\
+version: "MHMAKEVER"\n\
+Remarks and bug reports -> Marc Haesen\n\
+";
+ cerr << VersionStr;
+ exit(1);
+}
+///////////////////////////////////////////////////////////////////////////////
+makecommand::makecommand()
+{
+ char ExeName[MAX_PATH];
+#ifdef WIN32
+ GetModuleFileName(NULL,ExeName,sizeof(ExeName));
+ m_BuildCommand=ExeName;
+ transform(m_BuildCommand.begin(),m_BuildCommand.end(),m_BuildCommand.begin(),(int(__CDECL *)(int))tolower);
+#else
+ int NrChars=readlink ("/proc/self/exe", ExeName, sizeof(ExeName));
+ ExeName[NrChars]=0;
+ m_BuildCommand=ExeName;
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string Substitute(const string &ToSubst,const string &iSrcStr,const string &iToStr)
+{
+ string Ret=g_EmptyString;
+ matchres Res;
+ string SrcStr=iSrcStr;
+ string ToStr=iToStr;
+
+ if (string::npos==SrcStr.find('%'))
+ {
+ string PerStr("%");
+ SrcStr=PerStr+SrcStr;
+ ToStr=PerStr+ToStr;
+ }
+ const char *pTmp=ToSubst.c_str();
+ bool first=true;
+ while (*pTmp)
+ {
+ if (!first)
+ {
+ Ret+=g_SpaceString;
+ }
+ else
+ {
+ first=false;
+ }
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+
+ if (PercentMatch(Item,SrcStr,&Res))
+ {
+ Ret+=ReplaceWithStem(ToStr,Res.m_Stem);
+ }
+ else
+ {
+ Ret+=Item;
+ }
+ }
+
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+bool PercentMatch(const string &String,const string &Expr,matchres *pRes,const char Char)
+{
+ const char *pFirst=String.c_str();
+ const char *pFirstExpr=Expr.c_str();
+ while (*pFirstExpr && *pFirstExpr!=Char)
+ {
+ if (*pFirst!=*pFirstExpr)
+ return false;
+ pFirst++;
+ pFirstExpr++;
+ }
+
+ if (!*pFirstExpr)
+ {
+ if (!*pFirst)
+ {
+ if (pRes)
+ {
+ pRes->m_First=String;
+ pRes->m_Stem=pRes->m_Last=g_EmptyString;
+ }
+ return true;
+ } else
+ return false;
+ }
+ else if (!*pFirst)
+ return false;
+
+ const char *pEnd=pFirst+strlen(pFirst);
+
+ const char *pLast=pEnd;
+ const char *pLastExpr=pFirstExpr+strlen(pFirstExpr)-1;
+ if (pLastExpr!=pFirstExpr)
+ {
+ pLast--;
+
+ while (pLastExpr>pFirstExpr)
+ {
+ if (*pLastExpr!=*pLast)
+ return false;
+ pLastExpr--;
+ pLast--;
+ }
+ pLast++;
+ }
+ string Stem=string(pFirst,pLast-pFirst);
+
+ if (pRes)
+ {
+ pRes->m_First=string(String.c_str(),pFirst-String.c_str());
+
+ pRes->m_Stem=Stem;
+
+ pRes->m_Last=string(pLast,pEnd-pLast);
+ }
+ return true;
+}
+///////////////////////////////////////////////////////////////////////////////
+bool PercentMatchList(const string &String,const string &ExprList,matchres *pRes)
+{
+ const char *pTmp=ExprList.c_str();
+ while (*pTmp)
+ {
+ string Expr;
+ pTmp=NextItem(pTmp,Expr);
+ if (PercentMatch(String,Expr,pRes))
+ return true;
+ }
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+string ReplaceWithStem(const string &String,const string &Stem)
+{
+ string Ret=String;
+ int Pos=Ret.find('%');
+ while (Pos!=string::npos)
+ {
+ Ret=Ret.substr(0,Pos)+Stem+Ret.substr(Pos+1);
+ Pos=Ret.find('%');
+ }
+ return Ret;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+refptr<loadedmakefile> LOADEDMAKEFILES::find(const loadedmakefile &ToSearch)
+{
+ vector<refptr<loadedmakefile> >::const_iterator It=begin();
+ while (It!=end())
+ {
+ if (*(*It)==ToSearch)
+ return *It;
+ It++;
+ }
+ return refptr<loadedmakefile>();
+}
+
+LOADEDMAKEFILES g_LoadedMakefiles;
+
+///////////////////////////////////////////////////////////////////////////////
+loadedmakefile::loadedmakefile_statics::loadedmakefile_statics()
+{
+ m_GlobalCommandLineVars[MAKE]=g_MakeCommand;
+ const char *pEnv=getenv(MHMAKECONF);
+ if (pEnv)
+ {
+ m_GlobalCommandLineVars[MHMAKECONF]=pEnv;
+ m_MhMakeConf=GetFileInfo(pEnv);
+
+ // Get the revision of the working copy
+ // We do it the fastest way: this means just parsing the entries file in the .svn directory of the mhmakeconf directory
+ string EntriesFile=m_MhMakeConf->GetFullFileName()+OSPATHSEPSTR".svn"OSPATHSEPSTR"entries";
+ FILE *pFile=fopen(EntriesFile.c_str(),"r");
+ if (!pFile)
+ {
+ /* Entries file cannot be opened. Maybe it is not an svn working copy, so ignore it */
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo) cout<<"Warning: Assuming no subversion working copy: Error opening "<<EntriesFile<<endl;
+ #endif
+ return;
+ }
+
+ /* Check the format of this file, if it is 8 it is the new format else it is the old format. We do
+ * this by inspecting the first line of the file
+ */
+ char Line[255];
+ if (fgets(Line,sizeof(Line),pFile))
+ {
+ int iVersion=atoi(Line);
+ if (iVersion<8)
+ {
+ // This is the old format
+ while (fgets(Line,sizeof(Line),pFile))
+ {
+ if (!strncmp(Line," url=\"",8))
+ {
+ char *pUrl=Line+8+7;
+ char *pEnd=strchr(pUrl,'"');
+ #ifdef _DEBUG
+ if (!pEnd)
+ {
+ cerr<<"Format of entries file in .svn directory must have been changed. Update mhmake!\n";
+ exit(1);
+ }
+ #endif
+ *pEnd='\0';
+ m_GlobalCommandLineVars[WC_URL]=pUrl;
+ }
+ if (!strncmp(Line," revision=\"",13))
+ {
+ char *pRevision=Line+13;
+ char *pEnd=strchr(pRevision,'"');
+ #ifdef _DEBUG
+ if (!pEnd)
+ {
+ cerr<<"Format of entries file in .svn directory must have been changed. Update mhmake!\n";
+ exit(1);
+ }
+ #endif
+ *pEnd='\0';
+ m_GlobalCommandLineVars[WC_REVISION]=pRevision;
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* This is the new format */
+ fgets(Line,sizeof(Line),pFile);
+ fgets(Line,sizeof(Line),pFile);
+ if (fgets(Line,sizeof(Line),pFile))
+ {
+ Line[strlen(Line)-1]=0;
+ m_GlobalCommandLineVars[WC_REVISION]=Line;
+ }
+ if (fgets(Line,sizeof(Line),pFile))
+ {
+ Line[strlen(Line)-1]=0;
+ m_GlobalCommandLineVars[WC_URL]=Line+7;
+ }
+ }
+ }
+ fclose(pFile);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+loadedmakefile::loadedmakefile(vector<string> &Args,const string&Makefile)
+{
+ m_CommandLineVars=sm_Statics.m_GlobalCommandLineVars;
+
+ m_MakeDir=NullFileInfo;
+ vector<string>::iterator ArgIt=Args.begin();
+ while (ArgIt!=Args.end())
+ {
+ int EqPos=ArgIt->find('=');
+ if (EqPos!=string::npos)
+ {
+ string Var=ArgIt->substr(0,EqPos);
+ string Val=ArgIt->substr(EqPos+1);
+ m_CommandLineVars[Var]=Val;
+ }
+ else if ((*ArgIt)[0]=='-')
+ {
+ switch ((*ArgIt)[1])
+ {
+ case 'f':
+ if (ArgIt->size()>2)
+ {
+ if (!m_MakeDir)
+ {
+ m_Makefile=GetFileInfo(ArgIt->substr(2));
+ }
+ else
+ {
+ m_Makefile=GetFileInfo(ArgIt->substr(2),m_MakeDir);
+ }
+ }
+ else
+ {
+ ArgIt++;
+ if (!m_MakeDir)
+ {
+ m_Makefile=GetFileInfo(*ArgIt);
+ }
+ else
+ {
+ m_Makefile=GetFileInfo(*ArgIt,m_MakeDir);
+ }
+ }
+ break;
+ case 'C':
+#ifdef _DEBUG
+ if ((*ArgIt)[2]=='D')
+ {
+ g_CheckCircularDeps=true;
+ break;
+ }
+#endif
+ /* Fall through */
+ case 'c':
+ if (ArgIt->size()>2)
+ m_MakeDir=GetFileInfo(ArgIt->substr(2));
+ else
+ {
+ ArgIt++;
+ m_MakeDir=GetFileInfo(*ArgIt);
+ }
+ break;
+ case 'a':
+ g_RebuildAll=true;
+ break;
+ case 'q':
+ g_Quiet=true;
+ break;
+ case 's':
+ g_ForceAutoDepRescan=true;
+ break;
+ case 'v':
+ PrintVersionInfo();
+ break;
+#ifdef _DEBUG
+ case 'p':
+ g_PrintVarsAndRules=true;
+ break;
+ case 'n':
+ g_DoNotExecute=true;
+ break;
+ case 'w':
+ g_PrintAdditionalInfo=true;
+ break;
+ case 'd':
+ g_pPrintDependencyCheck=true;
+ break;
+ case 'D':
+ g_PrintMultipleDefinedRules=true;
+ break;
+ case 'l':
+ g_PrintLexYacc=true;
+ break;
+ case 'e':
+ g_DumpOnError=true;
+ break;
+ case 'm':
+ g_BuildMd5Db=true;
+ break;
+ case 'b':
+ g_GenProjectTree=true;
+ break;
+#endif
+ default:
+ cerr << "\nUnknown option: "<<*ArgIt<<endl<<endl;
+ cerr << s_UsageString;
+ throw(1);
+ }
+ }
+ else
+ {
+ m_CommandLineTargets.push_back(*ArgIt);
+ }
+ ArgIt++;
+ }
+ if (!m_Makefile)
+ {
+ if (!Makefile.empty())
+ {
+ if (!m_MakeDir)
+ m_Makefile=GetFileInfo(Makefile);
+ else
+ m_Makefile=GetFileInfo(Makefile,m_MakeDir);
+ }
+ }
+ if (!m_Makefile)
+ {
+ if (!m_MakeDir)
+ m_Makefile=GetFileInfo(m_CommandLineTargets[0]);
+ else
+ m_Makefile=GetFileInfo(m_CommandLineTargets[0],m_MakeDir);
+
+ m_CommandLineTargets.erase(m_CommandLineTargets.begin());
+ }
+ if (!m_MakeDir)
+ {
+ if (Makefile==g_EmptyString)
+ m_MakeDir=m_Makefile->GetDir(); /* This is one from load_makefile, so we take the directory of the makefile itself. */
+ else
+ m_MakeDir=curdir::GetCurDir(); /* This means that this is the main makefile given on the command line, so we take the current directory */
+ }
+
+ if (loadedmakefile::sm_Statics.m_MhMakeConf)
+ {
+ const string &RootDir=loadedmakefile::sm_Statics.m_MhMakeConf->GetFullFileName();
+ string MakeDir=m_MakeDir->GetFullFileName();
+ if (RootDir.length()>MakeDir.length() || _strnicmp(RootDir.c_str(),MakeDir.c_str(),RootDir.length()))
+ {
+ cerr<<"mhmake needs to run in a directory that is a subdirectory of the directory specified with %MHMAKECONF : "<<RootDir<<", make dir : "<<m_MakeDir->GetFullFileName()<<endl;
+ exit(1);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+void loadedmakefile::LoadMakefile()
+{
+ refptr<fileinfo> CurDir=curdir::GetCurDir();
+
+ curdir::ChangeCurDir(m_MakeDir);
+
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Loading makefile "<<m_Makefile->GetFullFileName()<<endl;
+ #endif
+
+ m_pParser=refptr<mhmakeparser>(new mhmakeparser(m_CommandLineVars));
+
+ // Add the MAKECMDGOALS environment variable
+ string MakeCmdGoals;
+ bool First=true;
+ vector<string>::iterator TarIt=m_CommandLineTargets.begin();
+ while (TarIt!=m_CommandLineTargets.end())
+ {
+ if (First)
+ First=false;
+ else
+ MakeCmdGoals+=g_SpaceString;
+ MakeCmdGoals+=*TarIt;
+ TarIt++;
+ }
+ m_pParser->SetVariable("MAKECMDGOALS",MakeCmdGoals);
+
+ string BaseAutoMak;
+
+ // First parse the makefile.before makefile which is in the directory $(MHMAKECONF) environment variable
+ refptr<fileinfo> DepFile;
+ if (sm_Statics.m_MhMakeConf)
+ {
+ BaseAutoMak=m_pParser->ExpandVar(BASEAUTOMAK);
+ if (BaseAutoMak.empty())
+ {
+ BaseAutoMak="makefile";
+ m_pParser->SetVariable(BASEAUTOMAK,BaseAutoMak);
+ }
+ refptr<fileinfo> BeforeMakefile=GetFileInfo(BaseAutoMak+".before",sm_Statics.m_MhMakeConf);
+
+ int result=m_pParser->ParseFile(BeforeMakefile,true);
+ if (result)
+ {
+ printf("Error parsing %s\n",BeforeMakefile->GetFullFileName().c_str());
+ throw(1);
+ }
+ m_pParser->UpdateDate(BeforeMakefile->GetDate());
+
+ // Now parse the automaticly generated dependency file, which needs to be in the objdir directory
+ string ObjDirName=m_pParser->ExpandExpression("$(OBJDIR)");
+ if (!ObjDirName.size())
+ {
+ printf("When making use of MHMAKECONF, you have to define OBJDIR in makefile.before");
+ throw(1);
+ }
+ DepFile=GetFileInfo(ObjDirName+OSPATHSEPSTR MAKEDEPFILE);
+ m_pParser->SetVariable(AUTODEPFILE,DepFile->GetFullFileName().c_str());
+ }
+ else
+ {
+ /* Create a file that is depending on the makefile name and the arguments */
+ md5_context ctx;
+
+ md5_starts( &ctx );
+
+ map<string,string>::const_iterator pIt=m_CommandLineVars.begin();
+ while (pIt!=m_CommandLineVars.end())
+ {
+ md5_update(&ctx, (uint8*)pIt->first.c_str(), pIt->first.size());
+ md5_update(&ctx, (uint8*)pIt->second.c_str(), pIt->second.size());
+ pIt++;
+ }
+ md5_update(&ctx, (uint8*)m_Makefile->GetFullFileName().c_str(), m_Makefile->GetFullFileName().size());
+
+ char ID[10];
+ sprintf(ID,"_%x",md5_finish32( &ctx));
+
+ DepFile=GetFileInfo(string(MAKEDEPFILE)+ID);
+ m_pParser->SetVariable(AUTODEPFILE,DepFile->GetFullFileName().c_str());
+ }
+
+ if (DepFile->Exists())
+ m_pParser->LoadAutoDepsFile(DepFile); /* Already load this autodep file before parsing of the makefile to avoid needless rebuilds. */
+
+ //m_pParser->yydebug=1;
+ int result=m_pParser->ParseFile(m_Makefile,true);
+ if (result)
+ {
+ printf("Error parsing %s\n",m_Makefile->GetFullFileName().c_str());
+ throw(1);
+ }
+ #ifdef _DEBUG
+ /* Check if the makefile has changed the AUTODEPFILE variable, if so generate a warning that a
+ * rebuild could happen for the rules defined for making included makefiles */
+ if (m_pParser->ExpandVar(AUTODEPFILE)!=DepFile->GetFullFileName())
+ {
+ cout << "\n\nWARNING:\n makefile '"<< m_Makefile->GetFullFileName() <<"' re-defines AUTODEPFILE\n from '"<< DepFile->GetFullFileName() <<"'\n to '"<<
+ m_pParser->ExpandVar(AUTODEPFILE) << "'\n (may cause needless rebuilds when having rules for included makefiles!!!!!)\n\n\n";
+ }
+
+ if (g_PrintAdditionalInfo)
+ {
+ if (m_pParser->GetFirstTarget())
+ cout<<"First target of "<<m_Makefile->GetFullFileName()<<" is "<<m_pParser->GetFirstTarget()->GetFullFileName()<<endl;
+ else
+ cout<<"No First target for "<<m_Makefile->GetFullFileName()<<endl;
+ }
+ #endif
+ m_pParser->UpdateDate(m_Makefile->GetDate());
+
+ if (sm_Statics.m_MhMakeConf)
+ {
+ refptr<fileinfo> AfterMakefile=GetFileInfo(BaseAutoMak+".after",sm_Statics.m_MhMakeConf);
+ int result=m_pParser->ParseFile(AfterMakefile);
+ if (result) {
+ printf("Error parsing %s\n",AfterMakefile->GetFullFileName().c_str());
+ throw(1);
+ }
+ m_pParser->UpdateDate(AfterMakefile->GetDate());
+ }
+ bool ForceAutoDepRescan=g_ForceAutoDepRescan;
+ if (DepFile->Exists())
+ m_pParser->LoadAutoDepsFile(DepFile);
+ else
+ ForceAutoDepRescan=true;
+ if (ForceAutoDepRescan)
+ m_pParser->EnableAutoDepRescan();
+
+ vector<string> &MakefilesToLoad=m_pParser->GetMakefilesToLoad();
+ vector<string>::iterator It=MakefilesToLoad.begin();
+ while (It!=MakefilesToLoad.end())
+ {
+ // First split the command into arguments
+ const char *pTmp=It->c_str();
+ vector<string> Args;
+ while (*pTmp)
+ {
+ string Item;
+ pTmp=NextItem(pTmp,Item);
+ Args.push_back(Item);
+ }
+
+ refptr<loadedmakefile> pLoadedMakefile(new loadedmakefile(Args));
+ refptr<loadedmakefile> Found=g_LoadedMakefiles.find(*pLoadedMakefile);
+ if (Found)
+ {
+ #ifdef _DEBUG
+ if (g_PrintAdditionalInfo)
+ cout << "Makefile already loaded: "<<Found->m_Makefile->GetFullFileName()<<endl;
+ #endif
+ }
+ else
+ {
+ g_LoadedMakefiles.push_back(pLoadedMakefile);
+
+ /* If there is a rule to build the makefile, first check if it needs to be rebuild */
+ m_pParser->BuildTarget(pLoadedMakefile->m_Makefile);
+ pLoadedMakefile->LoadMakefile();
+ }
+ It++;
+ }
+ curdir::ChangeCurDir(CurDir);
+
+ if (m_pParser->CompareEnv())
+ {
+ #ifdef _DEBUG
+ if (!g_GenProjectTree)
+ cout << "Rebuilding everything of "<< m_Makefile->GetFullFileName() <<" because environment and/or command-line variables have been changed.\n";
+ #endif
+ m_pParser->SetRebuildAll();
+ }
+
+}
+
+#ifdef _DEBUG
+///////////////////////////////////////////////////////////////////////////////
+void DumpVarsAndRules()
+{
+ int i;
+ LOADEDMAKEFILES::iterator LoadMakIt=g_LoadedMakefiles.begin();
+ while (LoadMakIt!=g_LoadedMakefiles.end())
+ {
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ cout << "Variables of makefile " << (*LoadMakIt)->m_Makefile->GetFullFileName() << endl;
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ (*LoadMakIt)->m_pParser->PrintVariables(true);
+ cout << endl;
+ LoadMakIt++;
+ }
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ cout << "All Rules\n";
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ PrintFileInfos();
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ cout << "All Implicit Rules\n";
+ for (i=0; i<80; i++) cout << "_";
+ cout << endl;
+ IMPLICITRULE::PrintImplicitRules();
+}
+#endif
+
diff --git a/tools/mhmake/src/util.h b/tools/mhmake/src/util.h
new file mode 100644
index 000000000..0b3005d54
--- /dev/null
+++ b/tools/mhmake/src/util.h
@@ -0,0 +1,229 @@
+/* This file is part of mhmake.
+ *
+ * Copyright (C) 2001-2009 Marc Haesen
+ *
+ * Mhmake is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Mhmake is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Mhmake. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+/* $Rev$ */
+
+#ifndef __UTIL_H__
+#define __UTIL_H__
+
+#include "fileinfo.h"
+
+// List of pre-defined variables
+#define WC_REVISION "WC_REVISION"
+#define WC_URL "WC_URL"
+#define AUTODEPFILE "AUTODEPFILE"
+#define OBJEXTVAR "OBJEXT"
+#define EXEEXTVAR "EXEEXT"
+#define SKIPHEADERS "SKIPHEADERS"
+#define MAKE "MAKE"
+#define MHMAKECONF "MHMAKECONF"
+#define BASEAUTOMAK "BASEAUTOMAK"
+#define CURDIR "CURDIR"
+#define USED_ENVVARS "USED_ENVVARS"
+#define PATH "PATH"
+#ifdef WIN32
+#define COMSPEC "COMSPEC"
+#define PYTHONEXE "python.exe"
+#define EXEEXT ".exe"
+#define OBJEXT ".obj"
+#define PLATFORM "win32"
+#else
+#define COMSPEC "SHELL"
+#define PYTHONEXE "python"
+#define EXEEXT ""
+#define OBJEXT ".o"
+#define PLATFORM "linux"
+#endif
+
+#define MHMAKEVER "1.3.14"
+
+#define MAKEDEPFILE ".makefile.dep"
+
+class makecommand
+{
+ string m_BuildCommand;
+public:
+ makecommand();
+ operator string()
+ {
+ return m_BuildCommand;
+ }
+};
+
+extern makecommand g_MakeCommand;
+
+///////////////////////////////////////////////////////////////////////////////
+inline const char *NextItem(const char *pTmp,string &Output, const char *pToks=" \t")
+{
+ const char *pStart;
+ const char *pStop;
+ while (strchr(pToks,*pTmp)&&*pTmp) pTmp++;
+ if (*pTmp=='"')
+ {
+ pStart=pTmp++;
+ while (*pTmp && *pTmp!='"') pTmp++;
+ if (*pTmp) pTmp++;
+ pStop=pTmp;
+ }
+ else if (*pTmp=='\'')
+ {
+ pStart=pTmp++;
+ while (*pTmp && *pTmp!='\'') pTmp++;
+ if (*pTmp) pTmp++;
+ pStop=pTmp;
+ }
+ else if (!*pTmp)
+ {
+ pStart=pStop=pTmp;
+ }
+ else
+ {
+ pStart=pTmp++;
+ #if OSPATHSEP=='/'
+ while (*pTmp)
+ {
+ if (!strchr(pToks,*pTmp) || (*(pTmp-1)=='\\'))
+ pTmp++;
+ else
+ break;
+ }
+ #else
+ while (!strchr(pToks,*pTmp)) pTmp++;
+ #endif
+ pStop=pTmp;
+ }
+ Output=string(pStart,pStop);
+ // skip trailing space
+ while (strchr(pToks,*pTmp)&&*pTmp) pTmp++;
+ return pTmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+inline const char *NextCharItem(const char *pTmp,string &Output,char Char)
+{
+ const char *pStart=pTmp;
+ while (*pTmp && *pTmp!=Char) pTmp++;
+ const char *pStop=pTmp;
+ if (*pTmp) pTmp++;
+
+ while (pStart<pStop && (*pStart==' ' || *pStart == '\t')) pStart++;
+ pStop--;
+ while (pStart<=pStop && (*pStop==' ' || *pStop == '\t')) pStop--;
+ pStop++;
+
+ Output=string(pStart,pStop);
+ return pTmp;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+string Substitute(const string &ToSubst,const string &SrcStr,const string &ToStr);
+
+struct matchres
+{
+ string m_First;
+ string m_Stem;
+ string m_Last;
+};
+
+bool PercentMatch(const string &String,const string &Expr,matchres *pRes=NULL,const char Char='%');
+bool PercentMatchList(const string &String,const string &ExprList,matchres *pRes=NULL);
+string ReplaceWithStem(const string &String,const string &Stem);
+
+struct loadedmakefile : public refbase
+{
+ struct loadedmakefile_statics
+ {
+ map<string,string> m_GlobalCommandLineVars;
+ refptr<fileinfo> m_MhMakeConf;
+
+ loadedmakefile_statics();
+ };
+ static loadedmakefile_statics sm_Statics;
+
+ refptr<fileinfo> m_Makefile;
+ refptr<fileinfo> m_MakeDir;
+ map<string,string> m_CommandLineVars;
+
+ vector<string> m_CommandLineTargets;
+ refptr<mhmakeparser> m_pParser;
+
+ loadedmakefile(vector<string> &Args,const string &Makefile=g_EmptyString);
+
+ void LoadMakefile();
+ void AddCommandLineVarsToEnvironment()
+ {
+ map<string,string>::const_iterator It=m_CommandLineVars.begin();
+ map<string,string>::const_iterator ItEnd=m_CommandLineVars.end();
+ while (It!=ItEnd)
+ {
+ sm_Statics.m_GlobalCommandLineVars.insert(*It++);
+ }
+ }
+
+ int operator==(const loadedmakefile &Other)
+ {
+ if (m_Makefile!=Other.m_Makefile)
+ return 0;
+ if (m_MakeDir!=Other.m_MakeDir)
+ return 0;
+ if (m_CommandLineTargets.size()!=Other.m_CommandLineTargets.size())
+ return 0;
+ if (m_CommandLineVars.size()!=Other.m_CommandLineVars.size())
+ return 0;
+ map<string,string>::iterator VarIt=m_CommandLineVars.begin();
+ while (VarIt!=m_CommandLineVars.end())
+ {
+ map<string,string>::const_iterator pFound=Other.m_CommandLineVars.find(VarIt->first);
+ if (pFound==Other.m_CommandLineVars.end())
+ return 0;
+ if (pFound->second!=VarIt->second)
+ return 0;
+ VarIt++;
+ }
+ vector<string>::iterator TarIt=m_CommandLineTargets.begin();
+ while (TarIt!=m_CommandLineTargets.end())
+ {
+ vector<string>::const_iterator OtherIt=Other.m_CommandLineTargets.begin();
+ while (OtherIt!=Other.m_CommandLineTargets.begin())
+ {
+ if (*TarIt==*OtherIt)
+ break;
+ OtherIt++;
+ }
+ if (OtherIt==Other.m_CommandLineTargets.end())
+ return 0;
+ TarIt++;
+ }
+ return 1;
+ }
+};
+
+class LOADEDMAKEFILES : public vector<refptr<loadedmakefile> >
+{
+public:
+ refptr<loadedmakefile> find(const loadedmakefile &pToSearch);
+ typedef vector<refptr<loadedmakefile> >::iterator iterator;
+};
+
+extern LOADEDMAKEFILES g_LoadedMakefiles;
+
+void DumpVarsAndRules();
+
+#endif
+