Catatan singkat tentang PVS Studio di CI (dan apa yang hilang)

Saya pikir tidak masuk akal sekali lagi untuk mengiklankan alat yang hebat untuk analisis statis - PVS Studio. Sudah ada banyak artikel tentang Habr yang didedikasikan untuknya, tetapi saya ingin menyentuh aspek lain - penggunaan alat ini dalam sistem integrasi berkelanjutan.


Jadi, ada beberapa organisasi, ada CI di dalamnya yang berfungsi sederhana: Jenkins menerima sebuah kait setelah dorongan pada Git, setelah itu meluncurkan beberapa saluran pipa. Karena alat yang digunakan, perakitan dilakukan untuk proyek yang dibuat dalam C # (msbuild) dan C ++ (msbuild, CMake). Pada salah satu tahap penyelesaian, pembuatan laporan dimulai, termasuk menggunakan Studio PVS (antara lain, cppcheck, tetapi ini tidak penting untuk narasi lebih lanjut).


PVS Studio memiliki alat analisis konsol yang diluncurkan dari baris perintah: PVS-Studio_Cmd.exe --target "${projectFile}" --output report.plog --progress


Pada input - nama proyek (.sln), pada output - laporan.


Laporkan - file dengan ekstensi .plog, adalah file XML biasa. Skema dokumen built-in, sehingga tidak ada kejutan dalam format output. Setidaknya sampai pengembang mengubah skema, tetapi kami tidak akan mempertimbangkan opsi ini.


Laporan ini terdiri dari serangkaian catatan, yang masing-masing menunjuk ke suatu file dan satu baris di dalamnya, kelas kesalahan, tingkat kesalahan, deskripsi, dan hal-hal lain yang tidak terlalu menarik.


Tetapi membaca XML dengan mata Anda adalah kesenangan bagi diri Anda sendiri, jadi Anda perlu beberapa cara untuk melihat dan menavigasi.


Yang paling sederhana dan paling berfungsi adalah plugin PVS Studio untuk Visual Studio, dengan kemampuan untuk menavigasi kode. Tetapi memaksa manajer teknis atau orang lain yang tertarik untuk memuat proyek ke dalam VS setiap saat adalah ide yang buruk, dan sejarah pengembangan proyek tidak terlihat.


Jadi mari kita pergi ke arah lain dan lihat apa yang bisa dilakukan. Dan ada cara yang cukup standar yang memungkinkan Anda untuk mengkonversi XML ke sesuatu yang lain: XSLT . Sekarang, mungkin, salah satu pembaca telah menyimpang, tetapi, bagaimanapun, saya mengusulkan untuk terus membaca.


XSLT adalah bahasa untuk mengonversi dokumen XML ke sesuatu yang lain. Itu hanya membandingkan aturan konversi ke templat masukan, tetapi kami telah melakukan konversi sendiri ke laporan HTML.


Saya harap tidak ada yang akan menilai saya untuk membuat tabel, karena data bersifat tabular. Setiap catatan dalam laporan akan sesuai dengan baris tabel dengan kolom berikut:


  1. Nomor baris dalam tabel.
  2. Nama file.
  3. Nomor baris.
  4. Kode kesalahan.
  5. Pesan kesalahan.

Nomor baris sesuai untuk referensi verbal dalam diskusi.


Nama file bersama dengan nama baris memungkinkan Anda untuk membuat tautan ke repositori. Tetapi lebih lanjut tentang itu nanti.


Kode kesalahan dibingkai oleh tautan ke situs web pengembang PVS-Studio: http://viva64.com/en/{ErrorCode } (atau / ru /, sesuka Anda).


Pesan kesalahan ada komentar.


Ada beberapa poin yang harus dihadapi.


Pertama, saya ingin pesan diurutkan berdasarkan tingkat kepentingan, serta memiliki jumlah total pesan dari masing-masing jenis. Tugas pertama diselesaikan menggunakan ekspresi xsl:sort , yang kedua adalah count([]) .


Kedua: nama file ditunjukkan secara penuh, dan nama relatif diperlukan untuk membuat tautan ke sistem kontrol versi. Anda hanya perlu memotong awalan yang sesuai dengan nama direktori dengan proyek di mana repositori telah dikloning (kami memiliki Git, tetapi mudah beradaptasi). Tetapi agar jalur ini muncul, kita perlu membuat parameter transformasi XSL menggunakan xsl:param construct. Sisanya relatif sederhana: hapus dari baris dengan nama file awalan umum dengan nama direktori tempat repositori dikloning. Saya harus mengatakan bahwa di XSLT masalah ini diselesaikan dengan cukup canggih.


Ketiga: validasi mengacu pada revisi spesifik dalam repositori, dan ini juga perlu diingat. Itu dipecahkan menggunakan parameter dengan pengidentifikasi komit. Begitu pula untuk cabang.
Keempat: jika Anda menggunakan perpustakaan pihak ketiga dengan kode sumber, jangan campur peringatan di dalamnya dengan peringatan di proyek kami. Masalahnya dipecahkan sebagai berikut: kami menempatkan semua proyek eksternal ke direktori tertentu, yang namanya tidak tercantum dalam proyek kami. Sekarang, jika nama file berisi subdirektori ini (sebenarnya hanya substring), maka entri dalam plog tidak masuk ke dalam laporan, tetapi dianggap sebagai "tersembunyi" di header laporan. Untuk fleksibilitas yang lebih besar, Anda dapat membuat parameter transformasi dan menetapkan nama default ke direktori ini: <xsl:param name="external" select="'External'" />


Nah dan satu lagi tugas kecil: untuk mengumpulkan tautan ke repositori. Kami menggunakan redmine + gitolite. Sekali lagi, mudah beradaptasi.


Karena banyak parameter konstan untuk konversi, Anda dapat menyiapkan awalan URL konstan:


 <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> 

Beberapa keindahan dengan gaya, dan Anda dapat menggunakannya. Kami menyematkan CSS di halaman, hanya untuk memiliki satu file laporan. Kami juga tidak perlu gambar.


Kode transformasi penuh di bawah spoiler


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> 

Kami memulai transformasi dengan bantuan utilitas konsol kecil yang ditulis dalam C #, tetapi Anda dapat melakukannya secara berbeda (jika perlu, saya juga akan membagikannya, tidak ada yang rumit dan rahasia di sana).
Dan kemudian Anda dapat membuat dasbor dari ini, tetapi ini, seperti yang mereka katakan, adalah kisah yang sama sekali berbeda.


Dan sekarang sedikit menangis ke arah pengembang. Ada satu bug, bukan fitur, yang membuatnya tidak mungkin untuk sepenuhnya melakukan apa yang dijelaskan di atas, apalagi, ini hanya berlaku untuk proyek-proyek C ++, di C # tidak ada masalah seperti itu. Saat file plog terbentuk, pada <File> nama selalu dilemparkan ke huruf kecil. Dan ketika redmine (dan web lainnya) di-host pada sistem mirip UNIX dengan nama file case-sensitive, case rusak ketika membentuk tautan ke file, yang membuat tautan tidak berfungsi. Sedih sekali.


Saya menerima jawaban untuk surat dukungan teknis bahwa perilaku ini disebabkan oleh API eksternal, tetapi tidak jelas mengapa sangat selektif dan hanya berlaku untuk C ++, dan tidak berlaku untuk C #.
Karena itu, untuk saat ini, seperti yang dijanjikan, saya menghimbau untuk kelanjutan Paull dan berharap untuk kerja sama yang bermanfaat.


Terima kasih atas perhatian anda


Pembaruan : sesuai dengan hasil korespondensi dengan pengembang yang diwakili oleh Paull dan khandeliants , penggalian dalam dilakukan, sebagai hasilnya beta dirilis di mana masalah yang dijelaskan di atas diselesaikan. Tetapi agar ini bekerja, dukungan untuk jalur pendek diperlukan, setidaknya pada drive tempat analisis dilakukan. Untuk melakukan ini, dalam registri di sepanjang jalan HKLM \ SYSTEM \ CurrentControlSet \ Control \ FileSystem, Anda perlu mengatur parameter NtfsDisable8dot3NameCreation (DWORD) ke nilai yang memungkinkan menyimpan nama file pendek. Lihat detail tentang MSDN .
Larangan standar pada nama pendek diperlukan untuk meningkatkan kecepatan NTFS.
Anda dapat mengatur nilai 0 dan tidak repot, atau 3 jika tugas CI dilakukan di profil pengguna di partisi sistem atau di tempat lain di partisi sistem, atau di 2 dan jalankan perintah fsutil 8dot3name set Z: 0 (disk Anda bukan Z :), di mana ruang kerja CI akan digunakan (juga berlaku untuk disk RAM, by the way).


Saya harap informasi ini muncul di suatu tempat di situs web viva64.


Sekarang sepertinya gestalt sudah ditutup, terima kasih lagi atas perhatian Anda.

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


All Articles