ملاحظة قصيرة حول PVS Studio في CI (وما هو مفقود)

أعتقد أنه من غير المنطقي مرة أخرى الإعلان عن أداة رائعة للتحليل الثابت - PVS Studio. هناك بالفعل الكثير من المقالات حول هبر مخصصة له ، لكني أريد أن أتطرق إلى جانب آخر - استخدام هذه الأداة في نظام تكامل مستمر.


لذا ، هناك بعض التنظيم ، هناك CI فيه يعمل ببساطة: يتلقى Jenkins خطافًا بعد دفعة في Git ، وبعد ذلك يطلق بعض خط الأنابيب. نظرًا للأدوات المستخدمة ، يتم تنفيذ التجميع للمشاريع التي تم إنشاؤها في C # (msbuild) و C ++ (msbuild ، CMake). في إحدى مراحل الإنهاء ، بدأ إنشاء التقرير ، بما في ذلك استخدام PVS Studio (من بين أمور أخرى ، cppcheck ، ولكن هذا ليس مهمًا لمزيد من السرد).


يحتوي PVS Studio على أداة تحليل وحدة التحكم التي يتم إطلاقها من سطر الأوامر: PVS-Studio_Cmd.exe --target "${projectFile}" --output report.plog --progress


عند الإدخال - اسم المشروع (.sln) ، عند الإخراج - التقرير.


تقرير - ملف بامتداد .plog ، هو ملف XML عادي. مخطط الوثيقة مدمج ، لذلك لا يمكن أن تكون هناك مفاجآت في تنسيق الإخراج. على الأقل حتى يغير المطورون المخطط ، لكننا لن نفكر في هذا الخيار.


يتكون التقرير من مجموعة من السجلات ، يشير كل منها إلى ملف وخط فيه ، وفئة خطأ ، ومستوى خطأ ، ووصف وأشياء أخرى غير مثيرة للاهتمام.


لكن قراءة XML بعيونك يسعدك ، لذا فأنت بحاجة إلى طريقة للعرض والتنقل.


أبسط وأكثر عمل هو البرنامج المساعد PVS Studio لـ Visual Studio ، مع القدرة على التنقل عبر التعليمات البرمجية. لكن إجبار مدير فني أو شخص مهتم آخر على تحميل مشروع في VS في كل مرة هو فكرة سيئة ، وتاريخ تطور المشروع غير مرئي.


لذلك دعونا نذهب في الاتجاه الآخر ونرى ما يمكن القيام به. وهناك طريقة قياسية إلى حد ما تسمح لك بتحويل XML إلى شيء آخر: XSLT . الآن ، على الأرجح ، قام أحد القراء بتشويه ، ولكن مع ذلك ، أقترح مواصلة القراءة.


XSLT هي لغة لتحويل مستندات XML إلى شيء آخر. يقارن ببساطة قاعدة التحويل بنموذج الإدخال ، لكننا قمنا بالتحويل إلى تقرير HTML لأنفسنا.


آمل ألا يحكم علي أحد على صنع الجداول ، لأن البيانات جدولة في طبيعتها. يتطابق كل سجل في التقرير مع صف جدول مع الأعمدة التالية:


  1. رقم الصف في الجدول.
  2. اسم الملف.
  3. رقم السطر.
  4. رمز الخطأ.
  5. رسالة الخطأ.

رقم السطر مناسب فقط للإشارة اللفظية في المناقشة.


يسمح لك اسم الملف مع اسم السطر بإنشاء ارتباط إلى المستودع. لكن المزيد عن ذلك لاحقًا.


يتم تأطير رمز الخطأ بواسطة رابط إلى موقع مطوري PVS-Studio: http://viva64.com/en/{ErrorCode } (أو / ru / ، كما تريد).


رسالة الخطأ لا تعليق.


هناك بعض النقاط للتعامل معها.


أولاً ، أرغب في فرز الرسائل حسب مستوى الأهمية ، وكذلك الحصول على إجمالي عدد الرسائل من كل نوع. يتم حل المهمة الأولى باستخدام تعبير xsl:sort ، والثانية هي count([]) .


ثانيًا: يشار إلى اسم الملف بالكامل ، وهناك حاجة إلى اسم نسبي لإنشاء ارتباط إلى نظام التحكم في الإصدار. تحتاج فقط إلى قطع البادئة المقابلة لاسم الدليل مع المشروع الذي تم استنساخ المستودع فيه (لدينا Git ، لكنه يتكيف بسهولة). ولكن لكي يظهر هذا المسار ، نحتاج إلى تحديد معلمات تحويل XSL باستخدام بنية xsl:param . الباقي بسيط نسبيًا: قم بإزالة البادئة الشائعة من السطر الذي يحتوي على اسم الملف باسم الدليل حيث يتم نسخ المستودع. يجب أن أقول أنه في XSLT يتم حل هذه المشكلة بشكل معقد للغاية.


ثالثًا: المصادقة تشير إلى مراجعة محددة في المستودع ، ويجب أيضًا مراعاة ذلك. يتم حلها باستخدام المعلمة مع معرف الالتزام. وبالمثل بالنسبة للفروع.
رابعًا: إذا كنت تستخدم مكتبات جهات خارجية برموز مصدر ، فلا تخلط بين التحذيرات فيها والتحذيرات في مشروعنا. يتم حل المشكلة على النحو التالي: نضع جميع المشاريع الخارجية في دليل معين ، لا يتم تضمين اسمها في مشروعنا. الآن ، إذا كان اسم الملف يحتوي على هذا الدليل الفرعي (في الواقع مجرد سلسلة فرعية) ، فإن الإدخال في المخطط لا يدخل في التقرير ، ولكنه يعتبر "مخفيًا" في رأس التقرير. لمزيد من المرونة ، يمكنك تحديد معلمات التحويل وتعيين اسم افتراضي لهذا الدليل: <xsl:param name="external" select="'External'" />


حسنًا ومهمة أخرى صغيرة: جمع الرابط إلى المستودع. نستخدم redmine + gitolite. مرة أخرى ، قابلة للتكيف.


نظرًا لأن العديد من المعلمات ثابتة للتحويل ، يمكنك إعداد بادئة عنوان URL ثابتة:


 <xsl:variable name="repo"> <xsl:text>http://redmine.your-site.com/projects/</xsl:text> <xsl:value-of select="$project" /> <xsl:text>/revisions/</xsl:text> <xsl:value-of select="$revision" /> <xsl:text>/entry/</xsl:text> </xsl:variable> 

بعض الجمال مع الأسلوب ، ويمكنك استخدامه. نقوم بتضمين CSS في الصفحة ، لمجرد الحصول على تقرير ملف واحد. نحن أيضا لا نحتاج إلى صور.


كود التحول الكامل تحت المفسد


XSLT
 <?xml version="1.0" encoding="utf-8"?> <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns="http://www.w3.org/1999/xhtml" exclude-result-prefixes="msxsl" > <xsl:output method="html" indent="yes"/> <xsl:param name="project" /> <xsl:param name="base-path" /> <xsl:param name="branch" select="'master'" /> <xsl:param name="revision" select="'[required]'" /> <xsl:param name="external" select="'External'" /> <xsl:variable name="repo"> <xsl:text>http://redmine.your-company.com/projects/</xsl:text> <!-- # !!!attention!!! # --> <xsl:value-of select="$project" /> <xsl:text>/revisions/</xsl:text> <xsl:value-of select="$revision" /> <xsl:text>/entry/</xsl:text> </xsl:variable> <xsl:template name="min-len"> <xsl:param name="a" /> <xsl:param name="b" /> <xsl:variable name="la" select="string-length($a)" /> <xsl:variable name="lb" select="string-length($b)" /> <xsl:choose> <xsl:when test="$la &lt; $lb"> <xsl:value-of select="$la"/> </xsl:when> <xsl:otherwise> <xsl:value-of select="$lb" /> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="strdiff-impl"> <xsl:param name="mask" /> <xsl:param name="value" /> <xsl:param name="n" /> <xsl:param name="lim" /> <xsl:choose> <xsl:when test="$n = $lim"> <xsl:value-of select="substring($value, $lim + 1)" /> </xsl:when> <xsl:when test="substring($mask, 0, $n) = substring($value,0, $n)"> <xsl:call-template name="strdiff-impl"> <xsl:with-param name="lim" select="$lim" /> <xsl:with-param name="mask" select="$mask" /> <xsl:with-param name="value" select="$value" /> <xsl:with-param name="n" select="$n + 1" /> </xsl:call-template> </xsl:when> <xsl:otherwise> <xsl:value-of select="substring($value, $n - 1)"/> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template name="strdiff"> <xsl:param name="mask" /> <xsl:param name="value" /> <xsl:choose> <xsl:when test="not($value)" /> <xsl:when test="not($mask)"> <xsl:value-of select="$value" /> </xsl:when> <xsl:otherwise> <xsl:call-template name="strdiff-impl"> <xsl:with-param name="mask" select="$mask" /> <xsl:with-param name="value" select="$value" /> <xsl:with-param name="lim"> <xsl:call-template name="min-len"> <xsl:with-param name="a" select="$mask" /> <xsl:with-param name="b" select="$value" /> </xsl:call-template> </xsl:with-param> <xsl:with-param name="n" select="1" /> </xsl:call-template> </xsl:otherwise> </xsl:choose> </xsl:template> <xsl:template match="/*"> <xsl:variable select="Solution_Path/SolutionPath" name="solution" /> <xsl:variable select="PVS-Studio_Analysis_Log [not(contains(File, $external))] [ErrorCode!='Renew'] " name="input" /> <html lang="en"> <head> <style type="text/css"> <![CDATA[ #report * {font-family: consolas, monospace, sans-serif; } #report {border-collapse: collapse; border: solid silver 1px;} #report th, #report td {padding: 6px 8px; border: solid silver 1px;} .sev-1 {background-color: #9A2617;} .sev-2 {background-color: #C2571A;} .sev-3 {background-color: #BCA136;} .sev-hidden {background-color: #999; } #report tbody * {color: white;} .fa * { color: #AAA; } a {color: #006;} .stat {padding: 20px;} .stat * {color: white; } .stat span {padding: 8px 16px; } html {background-color: #EEE;} .success {color: #3A3; } ]]> </style> </head> <body> <h1>PVS-Studio report</h1> <h2> <xsl:call-template name="strdiff"> <xsl:with-param name="value"> <xsl:value-of select="$solution" /> </xsl:with-param> <xsl:with-param name="mask"> <xsl:value-of select="$base-path" /> </xsl:with-param> </xsl:call-template> </h2> <div class="stat"> <span class="sev-1"> High: <b> <xsl:value-of select="count($input[Level=1])" /> </b> </span> <span class="sev-2"> Meduim: <b> <xsl:value-of select="count($input[Level=2])" /> </b> </span> <span class="sev-3"> Low: <b> <xsl:value-of select="count($input[Level=3])" /> </b> </span> <span class="sev-hidden" title="Externals etc"> Hidden: <b> <xsl:value-of select="count(PVS-Studio_Analysis_Log) - count($input)"/> </b> </span> </div> <xsl:choose> <xsl:when test="count($input) = 0"> <h2 class="success">No error messages.</h2> </xsl:when> <xsl:otherwise> <table id="report"> <thead> <tr> <th> # </th> <th> File </th> <th> Line </th> <th> Code </th> <th> Message </th> </tr> </thead> <tbody> <xsl:for-each select="$input"> <xsl:sort select="Level" data-type="number"/> <xsl:sort select="DefaultOrder" /> <tr> <xsl:attribute name="class"> <xsl:text>sev-</xsl:text> <xsl:value-of select="Level" /> <xsl:if test="FalseAlarm = 'true'"> <xsl:text xml:space="preserve"> fa</xsl:text> </xsl:if> </xsl:attribute> <th> <xsl:value-of select="position()" /> </th> <td> <xsl:variable name="file"> <xsl:call-template name="strdiff"> <xsl:with-param name="value" select="File" /> <xsl:with-param name="mask" select="$base-path" /> </xsl:call-template> </xsl:variable> <a href="{$repo}{translate($file, '\', '/')}#L{Line}"> <xsl:value-of select="$file" /> </a> </td> <td> <xsl:value-of select="Line"/> </td> <td> <a href="http://viva64.com/en/{ErrorCode}" target="_blank"> <xsl:value-of select="ErrorCode" /> </a> </td> <td> <xsl:value-of select="Message" /> </td> </tr> </xsl:for-each> </tbody> </table> </xsl:otherwise> </xsl:choose> </body> </html> </xsl:template> <xsl:template match="@* | node()"> <xsl:copy> <xsl:apply-templates select="@* | node()"/> </xsl:copy> </xsl:template> </xsl:stylesheet> 

نبدأ التحول بمساعدة أداة وحدة تحكم صغيرة مكتوبة بلغة C # ، ولكن يمكنك القيام بذلك بشكل مختلف (إذا لزم الأمر ، سأشاركه أيضًا ، لا يوجد شيء معقد وسري هناك).
ثم يمكنك إنشاء لوحة تحكم من هذا ، ولكن هذه ، كما يقولون ، قصة مختلفة تمامًا.


والآن صرخة صغيرة تجاه المطورين. هناك خطأ واحد ، وليس ميزة ، مما يجعل من المستحيل القيام بما هو موضح أعلاه بالكامل ، علاوة على ذلك ، هذا ينطبق فقط على مشاريع C ++ ، في C # لا توجد مثل هذه المشاكل. عندما يتم تكوين ملف plog ، في علامة <File> يتم دائمًا صب الاسم إلى أحرف صغيرة. وعندما تتم استضافة redmine (وغيرها من مواقع الويب) على أنظمة شبيهة بـ UNIX بأسماء ملفات حساسة لحالة الأحرف ، تنقطع الحالة عند تكوين روابط للملفات ، مما يجعل الروابط معطلة. يا له من حزن.


تلقيت إجابة على خطاب الدعم الفني بأن هذا السلوك يرجع إلى واجهة برمجة تطبيقات خارجية ، ولكن ليس من الواضح سبب كونه انتقائيًا للغاية وينطبق فقط على C ++ ، ولا ينطبق على C #.
لذلك ، حتى الآن ، كما وعدت ، أناشد استمرار بول وأتمنى تعاونا مثمرا.


شكرا لكم على اهتمامكم.


تحديث : وفقًا لنتائج المراسلات مع المطورين الممثلين من قبل Paull و khandeliants ، تم إجراء حفريات عميقة ، ونتيجة لذلك تم إصدار بيتا تم فيه حل المشكلة الموضحة أعلاه. ولكن لكي ينجح ذلك ، يلزم دعم المسارات القصيرة ، على الأقل على محرك الأقراص حيث يتم إجراء التحليل. للقيام بذلك ، في التسجيل على طول المسار HKLM \ SYSTEM \ CurrentControlSet \ Control \ FileSystem ، تحتاج إلى تعيين معلمة NtfsDisable8dot3NameCreation (DWORD) إلى قيمة تسمح بحفظ أسماء الملفات القصيرة. انظر التفاصيل على MSDN .
هناك حاجة إلى الحظر الافتراضي على الأسماء القصيرة لزيادة سرعة NTFS.
يمكنك إما تعيين القيمة 0 وعدم الإزعاج ، أو 3 إذا تم تنفيذ مهام CI في ملف تعريف المستخدم في قسم النظام أو في مكان آخر على قسم النظام ، أو في 2 وتشغيل الأمر fsutil 8dot3name set Z: 0 (القرص الخاص بك بدلاً من Z :) ، حيث سيتم نشر مساحة عمل CI (ينطبق أيضًا على أقراص RAM ، بالمناسبة).


آمل أن تظهر هذه المعلومات في مكان ما على موقع viva64 على الويب.


الآن يبدو أن الجشطالت مغلقة ، شكرًا مرة أخرى على اهتمامك.

Source: https://habr.com/ru/post/ar423829/


All Articles