diff --git a/Docs/src/compiler.but b/Docs/src/compiler.but index eb36fa7f..a8be1423 100644 --- a/Docs/src/compiler.but +++ b/Docs/src/compiler.but @@ -187,9 +187,9 @@ The pragma commands allows you to change compiler features and behavior. \c level | push | pop -This command will set the level of verbosity. 4=all, 3=no script, 2=no info, 1=no warnings, 0=none. +This command will set the level of verbosity: 4=all, 3=no script, 2=no info, 1=no warnings, 0=none. -Passing push will cause \R{verbose}{!verbose} to push the current verbosity level on a special stack. Passing pop will cause \R{verbose}{!verbose} to pop the current verbosity level from the same stack and use it. +Passing push will cause !verbose to push the current verbosity level on a special stack. Passing pop will cause !verbose to pop the current verbosity level from the same stack and use it. \c !verbose push \c !verbose 1 diff --git a/Docs/src/history.but b/Docs/src/history.but index 3345c6fc..fa2730f8 100644 --- a/Docs/src/history.but +++ b/Docs/src/history.but @@ -1,5 +1,13 @@ \A{history} Changelog and Release Notes +\H{v3.03} 3.03 + +Released on ??? ??rd, 20?? + +\S1{v3.03-cl} Changelog + +\S2{} Minor Changes + \H{v3.02.1} 3.02.1 Released on July 31st, 2017 diff --git a/Examples/Modern UI/MultiLanguage.nsi b/Examples/Modern UI/MultiLanguage.nsi index 776fcfa4..be9e2cd3 100644 --- a/Examples/Modern UI/MultiLanguage.nsi +++ b/Examples/Modern UI/MultiLanguage.nsi @@ -2,6 +2,8 @@ ;Multilingual Example Script ;Written by Joost Verburg +!pragma warning error all + ;-------------------------------- ;Include Modern UI diff --git a/Examples/languages.nsi b/Examples/languages.nsi index b95e0314..3a4d46ce 100644 --- a/Examples/languages.nsi +++ b/Examples/languages.nsi @@ -3,6 +3,8 @@ ; This is an example of a multilingual installer ; The user can select the language on startup +!pragma warning error all + ;-------------------------------- OutFile languages.exe diff --git a/Examples/makensis.nsi b/Examples/makensis.nsi index 6ea0ff25..5c8842aa 100644 --- a/Examples/makensis.nsi +++ b/Examples/makensis.nsi @@ -1,6 +1,9 @@ ;NSIS Setup Script ;-------------------------------- +!pragma warning error all +!pragma warning warning 7010 ; File /NonFatal + !ifdef VER_MAJOR & VER_MINOR !define /ifndef VER_REVISION 0 !define /ifndef VER_BUILD 0 diff --git a/Source/Tests/preprocessor.nsi b/Source/Tests/preprocessor.nsi index 9f25c9b0..83e47b07 100644 --- a/Source/Tests/preprocessor.nsi +++ b/Source/Tests/preprocessor.nsi @@ -12,6 +12,8 @@ code inside comments should not be executed */ # invalid preprocessor should be ignored !hello +!error valid_preprocessor_syntax_must_be_ignored +!define /foo /bar /baz and the same with invalid parameters !endif !ifdef d1 @@ -216,6 +218,21 @@ PageExEnd !insertmacro TEST_SCOPES "global" y n n n n +# test !pragma +!pragma warning push + !pragma warning disable 7000 + !include /NONFATAL doesnt_exist_nor_can_you_see_me.nsh +!pragma warning pop + +!pragma warning push + !pragma warning disable all + !include /NONFATAL doesnt_exist_nor_can_you_see_me.nsh + !pragma warning push + !pragma warning error all + !pragma warning pop + !warning "You can't see me" ; "disable all" is still in effect +!pragma warning pop + !else # this should just give a warning, not an error diff --git a/Source/build.cpp b/Source/build.cpp index 06776b47..9c26d48f 100644 --- a/Source/build.cpp +++ b/Source/build.cpp @@ -106,12 +106,13 @@ CEXEBuild::~CEXEBuild() } } -CEXEBuild::CEXEBuild(signed char pponly) : +CEXEBuild::CEXEBuild(signed char pponly, bool warnaserror) : preprocessonly(pponly), m_exehead(0), m_exehead_size(0) { set_verbosity(3); + if (warnaserror) diagstate.set_warning_as_error(); curlinereader=0; curfilename=0, linecnt=0; @@ -3410,6 +3411,7 @@ void CEXEBuild::set_verbosity(int lvl) int CEXEBuild::parse_pragma(LineParser &line) { const int rvSucc = PS_OK, rvWarn = PS_WARNING, rvErr = PS_WARNING; // rvErr is not PS_ERROR because we want !pragma parsing to be very forgiving. + const TCHAR badParamMsg[] = _T("Unknown pragma"); // 2.47 shipped with a corrupted CHM file (bug #1129). This minimal verification command exists because the !searchparse hack we added does not work with codepage 936! if (line.gettoken_enum(1, _T("verifychm\0")) == 0) @@ -3425,53 +3427,72 @@ int CEXEBuild::parse_pragma(LineParser &line) if (line.gettoken_enum(1, _T("warning\0")) == -1) return (warning_fl(DW_PP_PRAGMA_UNKNOWN, _T("Unknown pragma")), rvErr); - int warnOp = line.gettoken_enum(2, _T("disable\0enable\0default\0push\0pop\0")), ret = rvSucc; + enum { woperr = 0, wopwar, wopdis, wopena, wopdef, woppus, woppop, invalidwop }; + int warnOp = line.gettoken_enum(2, _T("error\0warning\0disable\0enable\0default\0push\0pop\0")), ret = rvSucc; if (warnOp < 0) - ret = rvErr, warning_fl(DW_PP_PRAGMA_UNKNOWN, _T("Unknown pragma")); // Unknown warning pragma action - else if (warnOp == 3) - diagstate.Push(); - else if (warnOp == 4) + ret = rvErr, warning_fl(DW_PP_PRAGMA_UNKNOWN, badParamMsg); // Unknown warning pragma action + else if (warnOp == woppus) // warning: push + diagstate.push(); + else if (warnOp == woppop) // warning: pop { - if (!diagstate.Pop()) + if (!diagstate.pop()) ret = rvWarn, warning_fl(DW_PP_PRAGMA_INVALID, _T("Unexpected")); } - else // warning: disable/enable/default + else // warning: error/warning/disable/enable/default { for (int ti = 3; ti < line.getnumtokens(); ++ti) { DIAGCODE code = static_cast(line.gettoken_int(ti)); - if (!diagstate.IsValidCode(code)) - ret = rvWarn, warning_fl(DW_PP_PRAGMA_INVALID, _T("Invalid number: \"%") NPRIs _T("\""), line.gettoken_str(ti)); - else if (warnOp == 0) - diagstate.Disable(code); - else // if ((warnOp == 1) | (warnOp == 2)) All warnings currently default to enabled - diagstate.Enable(code); + bool all = 0 == line.gettoken_enum(ti, _T("all\0")); + if (diagstate.is_valid_code(code)) + { + switch(warnOp) + { + case woperr: diagstate.error(code); break; + case wopwar: diagstate.warning(code); break; + case wopdis: diagstate.disable(code); break; + case wopena: diagstate.enable(code); break; + case wopdef: diagstate.def(code); break; + default: assert(0); + } + } + else + { + switch(all ? warnOp : invalidwop) + { + case woperr: diagstate.set_all(diagstate.werror); break; + case wopdis: diagstate.set_all(DiagState::wdisabled); break; + case wopena: diagstate.set_all(DiagState::wenabled); break; + case wopdef: diagstate.set_all(DiagState::get_default_state()); break; + default: ret = rvWarn, warning_fl(DW_PP_PRAGMA_INVALID, _T("Invalid number: \"%") NPRIs _T("\""), line.gettoken_str(ti)); + } + } } } return ret; } -void DiagState::Push() +void DiagState::push() { DiagState *p = new DiagState(); - p->m_Disabled = m_Disabled; // Copy current state - p->m_pStack = m_pStack, m_pStack = p; + *p = *this; // Copy the current state + p->m_pStack = m_pStack, m_pStack = p; // ...and push it on the stack } -bool DiagState::Pop() +bool DiagState::pop() { if (!m_pStack) return false; - DiagState *pPop = m_pStack; - m_pStack = pPop->m_pStack, pPop->m_pStack = 0; - m_Disabled.swap(pPop->m_Disabled); + DiagState *pPop = m_pStack; // Get the item on the top of the stack + *this = *pPop; // ...and assign it as the current state + pPop->m_pStack = 0; // The pop'ed item no longer owns the next item on the stack delete pPop; return true; } void CEXEBuild::warninghelper(DIAGCODE dc, bool fl, const TCHAR *fmt, va_list args) { - extern bool g_warnaserror; bool showcode = dc != DIAGCODE_INTERNAL_HIDEDIAGCODE; - if (diagstate.IsDisabled(dc)) return ; + if (diagstate.is_disabled(dc)) return ; + bool aserror = diagstate.is_error(dc); TCHAR codbuf[11+2+!0]; _stprintf(codbuf, showcode ? _T("%u: ") : _T(""), static_cast(dc)); @@ -3489,7 +3510,7 @@ void CEXEBuild::warninghelper(DIAGCODE dc, bool fl, const TCHAR *fmt, va_list ar m_warnings.add(msg,0); // Add to list of warnings to display at the end MakensisAPI::notify_e hostevent = MakensisAPI::NOTIFY_WARNING; - if (g_warnaserror) + if (aserror) hostevent = MakensisAPI::NOTIFY_ERROR, display_warnings = display_errors; notify(hostevent, msg); // Notify the host @@ -3497,7 +3518,7 @@ void CEXEBuild::warninghelper(DIAGCODE dc, bool fl, const TCHAR *fmt, va_list ar if (display_warnings) // Print "warning %msgwithcodeprefix%" or "warning: %msg%" PrintColorFmtMsg_WARN(_T("warning%") NPRIs _T("%") NPRIs _T("\n"), showcode ? _T(" ") : _T(": "), msg); - if (g_warnaserror) + if (aserror) { ERROR_MSG(_T("Error: warning treated as error\n")); extern int g_display_errors; diff --git a/Source/build.h b/Source/build.h index 74eeb2ae..f238e554 100644 --- a/Source/build.h +++ b/Source/build.h @@ -37,6 +37,7 @@ #include "tstring.h" #include +#include #ifdef NSIS_SUPPORT_STANDARD_PREDEFINES // Added by Sunil Kamath 11 June 2003 @@ -166,23 +167,45 @@ namespace MakensisAPI { #define FLAG_OFFSET(flag) (FIELD_OFFSET(exec_flags_t, flag)/sizeof(int)) class DiagState { + template struct mapped_type_helper { typedef typename M::value_type::second_type type; }; // VC6 uses referent_type and not mapped_type + template void insert_or_assign(C&c, const K&k, V val) + { + typename C::value_type item(k, val); + std::pair ret = c.insert(item); + if (!ret.second) ret.first->second = val; + } + template typename mapped_type_helper::type get_paired_value(const C&c, const K&k, typename mapped_type_helper::type defval) const + { + typename C::const_iterator it = c.find(k); + return c.end() == it ? defval : it->second; + } + template int get_code_state(T t, int def) const { return get_paired_value(m_Warnings, static_cast(t), def); } public: - DiagState() : m_pStack(0) { assert(DIAGCODE_INTERNAL_LAST <= 0xffff); } + typedef enum { wunspecified = -1, wdisabled = 0, wwarning, wenabled, werror } WARNSTATE; + DiagState() : m_pStack(0), m_FallbackState(get_default_state()) { assert(DIAGCODE_INTERNAL_LAST <= 0xffff); } ~DiagState() { delete m_pStack; } - void Enable(DIAGCODE n) { m_Disabled.erase(static_cast(n)); } - void Disable(DIAGCODE n) { m_Disabled.insert(static_cast(n)); } - bool IsDisabled(DIAGCODE n) { return m_Disabled.find(static_cast(n)) != m_Disabled.end(); } - void Push(); - bool Pop(); - static bool IsValidCode(unsigned int n) { return n >= DIAGCODE_INTERNAL_FIRST && n <= DIAGCODE_INTERNAL_LAST; } + static WARNSTATE get_default_state(DIAGCODE n = (DIAGCODE) 0) { return wenabled; } // All warnings currently default to enabled + void def(DIAGCODE n) { insert_or_assign(m_Warnings, static_cast(n), get_default_state(n)); } + void enable(DIAGCODE n) { insert_or_assign(m_Warnings, static_cast(n), wenabled); } + void disable(DIAGCODE n) { insert_or_assign(m_Warnings, static_cast(n), wdisabled); } + void warning(DIAGCODE n) { insert_or_assign(m_Warnings, static_cast(n), wwarning); } // Always !warning + void error(DIAGCODE n) { insert_or_assign(m_Warnings, static_cast(n), werror); } // Always !error + bool is_disabled(DIAGCODE n) const { return get_code_state(n, m_FallbackState) == wdisabled; } + bool is_error(DIAGCODE n) const { int s = get_code_state(n, m_FallbackState); return s == werror; } + void push(); + bool pop(); + void set_all(WARNSTATE wm) { m_Warnings.clear(); m_FallbackState = wm; } + void set_warning_as_error() { set_all(werror); } + static bool is_valid_code(unsigned int n) { return n >= DIAGCODE_INTERNAL_FIRST && n <= DIAGCODE_INTERNAL_LAST; } protected: DiagState *m_pStack; - std::set m_Disabled; + signed char m_FallbackState; // A fallback state so we don't have to fill the m_Warnings map with values for codes that are not explicitly set by the user + std::map m_Warnings; }; class CEXEBuild { public: - CEXEBuild(signed char pponly); + CEXEBuild(signed char pponly, bool warnaserror); void initialize(const TCHAR *makensis_path); ~CEXEBuild(); diff --git a/Source/makenssi.cpp b/Source/makenssi.cpp index 95684309..55c26031 100644 --- a/Source/makenssi.cpp +++ b/Source/makenssi.cpp @@ -37,7 +37,7 @@ using namespace std; NSISRT_DEFINEGLOBALS(); -bool g_dopause=false, g_warnaserror=false; +bool g_dopause=false; NStreamEncoding g_outputenc; #ifdef _WIN32 UINT g_wincon_orgoutcp; @@ -307,7 +307,7 @@ static inline int makensismain(int argc, TCHAR **argv) NStreamEncoding inputenc, &outputenc = g_outputenc; int argpos=0; bool do_cd=true, noconfig=false; - bool no_logo=true; + bool no_logo=true, warnaserror=false; bool initialparsefail=false, in_files=false; bool oneoutputstream=false; signed char pponly=0; @@ -377,7 +377,7 @@ static inline int makensismain(int argc, TCHAR **argv) } else if (!_tcsicmp(swname,_T("WX"))) { - g_warnaserror = true; + warnaserror = true; } // This must be parsed last because it will eat other switches else if (S7IsChEqualI('o',swname[0]) && swname[1]) stdoutredirname=swname+1; @@ -423,7 +423,7 @@ static inline int makensismain(int argc, TCHAR **argv) unsigned int files_processed=0; unsigned int cmds_processed=0; - CEXEBuild build(pponly); + CEXEBuild build(pponly, warnaserror); try { build.initialize(argv[0]);