Added preprocess only mode (/[SAFE]PPO switch)

git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@6495 212acab6-be3b-0410-9dea-997c60f758d6
This commit is contained in:
anders_k 2014-06-19 19:06:49 +00:00
parent d91176ba49
commit 0bffaecea3
9 changed files with 99 additions and 38 deletions

View file

@ -8,6 +8,8 @@ Released on ?, 2014
\S2{} Major Changes
\b Added PPO and SafePPO preprocess-only compiler switches
\b MakeNSIS WM_COPYDATA messages now use the QH_OUTPUTCHARSET encoding with CP_ACP as the default for compatibility with old IDEs.
\S2{} Minor Changes

View file

@ -33,6 +33,8 @@ If you want to use MakeNSIS on the command line, the syntax of the makensis comm
\b /OUTPUTCHARSET allows you to specify the codepage used by stdout when the output is redirected. (\NsisOutputCharset)
\b /PPO or /SAFEPPO will only run the preprocessor and print the result to stdout. The safe version will not execute instructions like !appendfile or !system. !packhdr and !finalize are never executed.
\b Using the /D switch one or more times will add to symbols to the globally defined list (See !define).
\b Using the /X switch one or more times will execute the code you specify following it. Example: "/XAutoCloseWindow false"

View file

@ -105,15 +105,15 @@ CEXEBuild::~CEXEBuild()
}
}
CEXEBuild::CEXEBuild() :
m_exehead(0),
m_exehead_size(0)
CEXEBuild::CEXEBuild(signed char pponly) :
m_exehead(0),
m_exehead_size(0),
preprocessonly(pponly)
{
set_verbosity(3);
curlinereader=0;
curfilename=0;
linecnt=0;
curfilename=0, linecnt=0;
cur_ifblock=NULL;
last_line_had_slash=0;
inside_comment=false;
@ -3366,6 +3366,7 @@ int CEXEBuild::get_verbosity() const
void CEXEBuild::set_verbosity(int lvl)
{
if (preprocessonly) lvl = STD_MIN(lvl, 1);
display_errors = lvl > 0;
display_warnings = lvl > 1;
display_info = lvl > 2;

View file

@ -102,7 +102,7 @@ namespace MakensisAPI {
class CEXEBuild {
public:
CEXEBuild();
CEXEBuild(signed char pponly);
void initialize(const TCHAR *makensis_path);
~CEXEBuild();
@ -135,7 +135,7 @@ class CEXEBuild {
// process a script (you can process as many scripts as you want,
// it is as if they are concatenated)
int process_script(NIStream&Strm, const TCHAR *filename);
int process_oneline(TCHAR *line, const TCHAR *curfilename, int lineptr);
int process_oneline(const TCHAR *line, const TCHAR *curfilename, int lineptr);
// you only get to call write_output once, so use it wisely.
int write_output(void);
@ -145,6 +145,7 @@ class CEXEBuild {
DefineList definedlist; // List of identifiers marked as "defined" like
// C++ macro definitions such as _UNICODE.
void define(const TCHAR *p, const TCHAR *v=_T("")); // to add a defined thing.
signed char preprocessonly; // > 0 = safe, < 0 = unsafe
int get_verbosity() const;
void set_verbosity(int lvl);
@ -173,6 +174,8 @@ class CEXEBuild {
// tokens.cpp
bool is_ppbranch_token(TCHAR *s);
bool is_pp_token(int tkid);
bool is_unsafe_pp_token(int tkid);
int get_commandtoken(TCHAR *s, int *np, int *op, int *pos);
const TCHAR* get_commandtoken_name(int tok);

View file

@ -98,3 +98,12 @@ void GrowBuf::resize(int newlen)
int GrowBuf::getlen() const { return m_used; }
void *GrowBuf::get() const { return m_s; }
void GrowBuf::swap(GrowBuf&other)
{
std::swap(m_s, other.m_s);
std::swap(m_alloc, other.m_alloc);
std::swap(m_used, other.m_used);
std::swap(m_zero, other.m_zero);
std::swap(m_bs, other.m_bs);
}

View file

@ -103,6 +103,8 @@ class GrowBuf : public IGrowBuf
*/
void *get() const;
void swap(GrowBuf&other);
private:
void *m_s; /* the storage buffer */
int m_alloc; /* allocated bytes */

View file

@ -176,6 +176,7 @@ static void print_usage()
#ifdef _WIN32
_T(" ") OPT_STR _T("OUTPUTCHARSET <") TSTR_OUTPUTCHARSET _T(">\n")
#endif
_T(" ") OPT_STR _T("[SAFE]PPO preprocess to stdout/file\n")
_T(" ") OPT_STR _T("Ddefine[=value] defines the symbol \"define\" for the script [to value]\n")
_T(" ") OPT_STR _T("Xscriptcmd executes scriptcmd in script (i.e. \"") OPT_STR _T("XOutFile inst.exe\")\n")
_T(" ") _T(" parameters are processed by order (") OPT_STR _T("Ddef ins.nsi != ins.nsi ") OPT_STR _T("Ddef)\n")
@ -298,6 +299,7 @@ static inline int makensismain(int argc, TCHAR **argv)
bool no_logo=true;
bool initialparsefail=false;
bool noconfig=false;
signed char pponly=0;
#ifdef _WIN32
signed char outputbom=1;
@ -352,6 +354,10 @@ static inline int makensismain(int argc, TCHAR **argv)
outputenc.SetCodepage(NStreamEncoding::UTF16LE);
}
#endif
else if (!_tcsicmp(swname,_T("PPO")) || !_tcsicmp(swname,_T("SafePPO")))
{
pponly = S7IsChEqualI('s',swname[0]) ? 1 : -1;
}
else if (S7IsChEqualI('v',swname[0]) && swname[1] && !swname[2])
{
no_logo=swname[1] >= _T('0') && swname[1] <= _T('2');
@ -394,7 +400,7 @@ static inline int makensismain(int argc, TCHAR **argv)
unsigned int files_processed=0;
unsigned int cmds_processed=0;
CEXEBuild build;
CEXEBuild build(pponly);
try
{
build.initialize(argv[0]);
@ -421,7 +427,7 @@ static inline int makensismain(int argc, TCHAR **argv)
fflush(g_output);
return 0;
}
if (!no_logo) print_logo();
if (!no_logo && !pponly) print_logo();
argpos=initialparsefail ? argc : 1;
@ -431,15 +437,17 @@ static inline int makensismain(int argc, TCHAR **argv)
in_files=1;
else if (IS_OPT(argv[argpos]) && _tcscmp(argv[argpos], _T("-")) && !in_files)
{
if (!_tcsicmp(&argv[argpos][1],_T("NOCD"))) do_cd=false;
else if (!_tcsicmp(&argv[argpos][1],_T("NOCONFIG"))) noconfig=true;
else if (!_tcsicmp(&argv[argpos][1],_T("PAUSE"))) g_dopause=true;
else if (!_tcsicmp(&argv[argpos][1],_T("LICENSE")))
const TCHAR* const swname = &argv[argpos][1];
if (!_tcsicmp(swname,_T("PPO")) || !_tcsicmp(swname,_T("SafePPO"))) {} // Already parsed
else if (!_tcsicmp(swname,_T("NOCD"))) do_cd=false;
else if (!_tcsicmp(swname,_T("NOCONFIG"))) noconfig=true;
else if (!_tcsicmp(swname,_T("PAUSE"))) g_dopause=true;
else if (!_tcsicmp(swname,_T("LICENSE")))
{
if (build.display_info) print_license();
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("CMDHELP")))
else if (!_tcsicmp(swname,_T("CMDHELP")))
{
if (argpos < argc-1)
build.print_help(argv[++argpos]);
@ -447,12 +455,12 @@ static inline int makensismain(int argc, TCHAR **argv)
build.print_help(NULL);
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("HDRINFO")))
else if (!_tcsicmp(swname,_T("HDRINFO")))
{
print_stub_info(build);
nousage++;
}
else if (!_tcsicmp(&argv[argpos][1],_T("INPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("ICS")))
else if (!_tcsicmp(swname,_T("INPUTCHARSET")) || !_tcsicmp(swname,_T("ICS")))
{
if (!HasReqParam(argv, ++argpos, argc)) break;
WORD cp = GetEncodingFromString(argv[argpos]);
@ -464,13 +472,13 @@ static inline int makensismain(int argc, TCHAR **argv)
}
inputenc.SafeSetCodepage(cp);
}
else if (S7IsChEqualI('v',argv[argpos][1]) &&
else if (S7IsChEqualI('v',*swname) &&
argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('4') && !argv[argpos][3])
{
int v=argv[argpos][2]-_T('0');
build.set_verbosity(v);
}
else if (S7IsChEqualI('p',argv[argpos][1]) &&
else if (S7IsChEqualI('p',*swname) &&
argv[argpos][2] >= _T('0') && argv[argpos][2] <= _T('5') && !argv[argpos][3])
{
#ifdef _WIN32
@ -496,10 +504,10 @@ static inline int makensismain(int argc, TCHAR **argv)
#endif
}
// Already parsed these (must adjust argpos)
else if (!_tcsicmp(&argv[argpos][1],_T("NOTIFYHWND"))) ++argpos;
else if (!_tcsicmp(&argv[argpos][1],_T("OUTPUTCHARSET")) || !_tcsicmp(&argv[argpos][1],_T("OCS"))) ++argpos;
else if (!_tcsicmp(swname,_T("NOTIFYHWND"))) ++argpos;
else if (!_tcsicmp(swname,_T("OUTPUTCHARSET")) || !_tcsicmp(swname,_T("OCS"))) ++argpos;
// These must be parsed last because they will eat other switches
else if (S7IsChEqualI('d',argv[argpos][1]) && argv[argpos][2])
else if (S7IsChEqualI('d',swname[0]) && swname[1])
{
TCHAR *p=argv[argpos]+2;
TCHAR *s=_tcsdup(p),*v;
@ -509,9 +517,9 @@ static inline int makensismain(int argc, TCHAR **argv)
build.define(s,v?v:_T(""));
free(s);
}
else if (S7IsChEqualI('x',argv[argpos][1]) && argv[argpos][2])
else if (S7IsChEqualI('x',swname[0]) && swname[1])
{
if (build.process_oneline(argv[argpos]+2,_T("<command line>"),argpos+1) != PS_OK)
if (build.process_oneline(swname+1,_T("<command line>"),argpos+1) != PS_OK)
{
return 1;
}
@ -519,9 +527,9 @@ static inline int makensismain(int argc, TCHAR **argv)
}
// Already parsed these ("VERSION" never gets this far)
#ifdef _WIN32
else if (!_tcsicmp(&argv[argpos][1],_T("RAW"))) {}
else if (!_tcsicmp(swname,_T("RAW"))) {}
#endif
else if (S7IsChEqualI('o',argv[argpos][1]) && argv[argpos][2]) {}
else if (S7IsChEqualI('o',swname[0]) && swname[1]) {}
else
break;
}
@ -615,6 +623,8 @@ static inline int makensismain(int argc, TCHAR **argv)
return 1;
}
if (build.preprocessonly) return 0;
if (build.display_info)
{
_ftprintf(g_output,_T("\nProcessed "));

View file

@ -286,6 +286,8 @@ int CEXEBuild::process_script(NIStream&Strm, const TCHAR *filename)
}
#define PRINTHELP() { print_help(line.gettoken_str(0)); return PS_ERROR; }
static void PREPROCESSONLY_BEGINCOMMENT() { extern FILE *g_output; _ftprintf(g_output,_T("!if 0 /*\n")); }
static void PREPROCESSONLY_ENDCOMMENT() { extern FILE *g_output; _ftprintf(g_output,_T("*/\n!endif\n")); }
void CEXEBuild::start_ifblock()
{
@ -369,6 +371,8 @@ int CEXEBuild::doParse(const TCHAR *str)
}
}
GrowBuf ppoline;
if (preprocessonly) m_linebuild.swap(ppoline); // LineParser strips quotes and we need to display them
m_linebuild.resize(0);
if (res)
@ -393,7 +397,11 @@ parse_again:
ERROR_MSG(_T("Invalid label: %") NPRIs _T(" (labels cannot begin with !, $, -, +, or 0-9)\n"),line.gettoken_str(0));
return PS_ERROR;
}
if (add_label(line.gettoken_str(0))) return PS_ERROR;
extern FILE *g_output;
if (preprocessonly)
_ftprintf(g_output,_T("%") NPRIs _T("\n"),line.gettoken_str(0));
else
if (add_label(line.gettoken_str(0))) return PS_ERROR;
line.eattoken();
goto parse_again;
}
@ -616,6 +624,16 @@ parse_again:
}
if (!cur_ifblock || (!cur_ifblock->ignore && !cur_ifblock->inherited_ignore))
{
if (preprocessonly)
{
extern FILE *g_output;
bool pptok = is_pp_token(tkid), docmd = pptok;
bool both = TOK_P_VERBOSE == tkid || TOK_P_WARNING == tkid || TOK_P_ECHO == tkid;
if (TOK_P_FINALIZE == tkid || TOK_P_PACKEXEHEADER == tkid) docmd = false;
if (docmd && is_unsafe_pp_token(tkid) && preprocessonly > 0) docmd = false;
if (!docmd || both) _ftprintf(g_output,(_T("%") NPRIs _T("\n")),ppoline.get());
if (!docmd && !both) return PS_OK;
}
return doCommand(tkid,line);
}
@ -1006,7 +1024,7 @@ l_errwcconv:
return PS_OK;
}
int CEXEBuild::process_oneline(TCHAR *line, const TCHAR *filename, int linenum)
int CEXEBuild::process_oneline(const TCHAR *line, const TCHAR *filename, int linenum)
{
const TCHAR *last_filename = curfilename;
int last_linecnt = linecnt;
@ -3129,6 +3147,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
}
if (!validparams || comp == -1) PRINTHELP()
SCRIPT_MSG(_T("%") NPRIs _T(": \"%") NPRIs _T("\"\n"),cmdname,exec);
PREPROCESSONLY_BEGINCOMMENT();
#ifdef _WIN32
if (TOK_P_EXECUTE == which_token)
{
@ -3164,6 +3183,7 @@ int CEXEBuild::doCommand(int which_token, LineParser &line)
ERROR_MSG(_T("%") NPRIs _T(": returned %d, aborting\n"),cmdname,ret);
return PS_ERROR;
}
PREPROCESSONLY_ENDCOMMENT();
SCRIPT_MSG(_T("%") NPRIs _T(": returned %d\n"),cmdname,ret);
}
return PS_OK;

View file

@ -335,10 +335,29 @@ bool CEXEBuild::is_ppbranch_token(TCHAR *s)
case TOK_P_IFDEF: case TOK_P_IFNDEF:
case TOK_P_IFMACRODEF: case TOK_P_IFMACRONDEF:
return true;
default: return false;
default:
return false;
}
}
bool CEXEBuild::is_pp_token(int tkid)
{
// NOTE: This assumes that all TOK_P_* in tokens.h are grouped together.
return (tkid >= TOK_P_IF && tkid <= TOK_P_SEARCHREPLACESTRING);
}
bool CEXEBuild::is_unsafe_pp_token(int tkid)
{
switch(tkid)
{
case TOK_P_TEMPFILE: case TOK_P_APPENDFILE: case TOK_P_DELFILE:
case TOK_P_SYSTEMEXEC: case TOK_P_EXECUTE: case TOK_P_FINALIZE:
case TOK_P_PACKEXEHEADER:
return true;
}
return false;
}
int CEXEBuild::get_commandtoken(TCHAR *s, int *np, int *op, int *pos)
{
for (int x = 0; x < TOK__LAST; x ++)
@ -355,16 +374,7 @@ int CEXEBuild::get_commandtoken(TCHAR *s, int *np, int *op, int *pos)
int CEXEBuild::GetCurrentTokenPlace()
{
if (build_cursection)
{
if (build_cursection_isfunc)
{
return TP_FUNC;
}
else
{
return TP_SEC;
}
}
return build_cursection_isfunc ? TP_FUNC : TP_SEC;
if (cur_page)
return TP_PAGEEX;
@ -374,6 +384,8 @@ int CEXEBuild::GetCurrentTokenPlace()
int CEXEBuild::IsTokenPlacedRight(int pos, TCHAR *tok)
{
if (preprocessonly)
return PS_OK;
if ((unsigned int) pos > (sizeof(tokenlist) / sizeof(tokenType)))
return PS_OK;