Building PartCover 4 on Vista
Updated again: PartCover Revision 35 includes a patch I provided that builds the same functionality in C#, transiently registering the COM component in HKCU during build, and also providing a --register
command line option which is equivalent to the old nCover //r
option.
Updated with new complete registry frobbing script, based on actually diffing the before and after states of HKCR when using regsvr32 on the PartCover dll.
Following on from earlier in the week, I pulled the current trunk from SourceForge, and set about building it then putting it to work on some F# code which had caused an earlier version (the most recent available at the end of last year) to balk.
Unzipping the tools (Boost, ATL server) into the appropriate libraries, guided by the include paths (and moving the Debug relative paths to match the Release ones) was trivial, and then it's just a case of firing up MSBuild. Or so I thought.
First hiccup -- it attempts to register the PartCover assembly during the build, and I don't run as administrator by default. So I replaced the
regsvr32 /s /c "$(TargetPath)"
by a manual registry frobbing trick (akin to what NCover 1.5.8 used for its //r
option)
"$(ProgramFiles)\FSharp-2.0.0.0\bin\fsi.exe" "$(ProjectDir)\partcover.fsx" "$(TargetPath)"
where partcover.fsx
is
open System | |
open System.IO | |
open Microsoft.Win32 | |
type Key = string * list<(string * string)> | |
type Keys = Key * list<Key> | |
let name = "\0" | |
let dir = "\1" | |
let key1 = (@"Software\Classes\AppID\7D0E6AAB-C5FC-4103-AAD4-8BF3112A56C4", [ (null, "PartCoverCorDriver") ] ) | |
let key2 = (@"Software\Classes\AppID\PartCoverCorDriver.DLL", [ ("AppId", "7d0e6aab-c5fc-4103-aad4-8bf3112a56c4") ] ) | |
let key3 = (@"Software\Classes\CLSID\{717FF691-2ADF-4AC0-985F-1DD3C42FDF90}", | |
[ (null, "CorProfiler Object") ; ("AppId", "7d0e6aab-c5fc-4103-aad4-8bf3112a56c4")] ) | |
let key3a = ("InprocServer32", [(null, name); ("ThreadingModel", "Both")]) | |
let key3b = ("ProgID", [(null, "PartCover.CorDriver.CorProfiler.4")]) | |
let key3c = ("Programmable", []) | |
let key3d = ("TypeLib", [(null, "{7D0E6AAB-C5FC-4103-AAD4-8BF3112A56C4}")]) | |
let key3e = ("VersionIndependentProgID", [(null, "PartCover.CorDriver.CorProfiler")]) | |
let key4 = (@"Software\Classes\CLSID\{FB20430E-CDC9-45D7-8453-272268002E08}", [(null, "PartCoverConnector2 Object"); ("AppId", "7d0e6aab-c5fc-4103-aad4-8bf3112a56c4")]) | |
let key4a = ("InprocServer32", [(null, name); ("ThreadingModel", "Both")]) | |
let key4b = ("ProgID", [(null, "PartCover.CorDriver.Connector.3")]) | |
let key4c = ("Programmable", []) | |
let key4d = ("TypeLib", [null, "{7D0E6AAB-C5FC-4103-AAD4-8BF3112A56C4}"] ) | |
let key4e = ("VersionIndependentProgID", [(null, "PartCover.CorDriver.Connector")]) | |
let key5 = (@"Software\Classes\PartCover.CorDriver.Connector", [(null, "PartCoverConnector2 Object")]) | |
let key5a = ("CLSID", [(null, "{FB20430E-CDC9-45D7-8453-272268002E08}")]) | |
let key5b = ("CurVer", [(null, "PartCover.CorDriver.Connector.3")]) | |
let key6 = (@"Software\Classes\PartCover.CorDriver.Connector.3", [(null, "PartCoverConnector2 Object")]) | |
let key6a = ("CLSID", [(null, "{FB20430E-CDC9-45D7-8453-272268002E08}")]) | |
let key7 = (@"Software\Classes\PartCover.CorDriver.CorProfiler", [(null, "CorProfiler Object")]) | |
let key7a = ("CLSID", [(null, "{717FF691-2ADF-4AC0-985F-1DD3C42FDF90}")]) | |
let key7b = ("CurVer", [(null, "PartCover.CorDriver.CorProfiler.4")]) | |
let key8 = (@"Software\Classes\PartCover.CorDriver.CorProfiler.4", [(null, "CorProfiler Object")]) | |
let key8a = ("CLSID", [(null, "{717FF691-2ADF-4AC0-985F-1DD3C42FDF90}")]) | |
let key9 = (@"Software\Classes\TypeLib\{7d0e6aab-c5fc-4103-aad4-8bf3112a56c4}\4.0", [(null, "PartCover module")]) | |
let key9a = ("0\win32", [(null, name)]) | |
let key9b = ("FLAGS", [(null, "0")]) | |
let key9c = ("HELPDIR", [(null, dir)]) | |
let keys = [(key1, []); | |
(key2, []); | |
(key3, [key3a; key3b; key3c; key3d; key3e]); | |
(key4, [key4a; key4b; key4c; key4d; key4e]); | |
(key5, [key5a; key5b]); | |
(key6, [key6a]); | |
(key7, [key7a; key7b]); | |
(key8, [key8a]); | |
(key9, [key9a; key9b; key9c])] | |
//------------------------------------------- | |
let ReMap (filename:string) (value :(string * string)) = | |
match value with | |
| (l1, "\0") -> (l1, filename) | |
| (l2, "\1") -> | |
let where = new FileInfo(filename) | |
(l2, where.DirectoryName) | |
| _ -> value | |
let MakeSubKey (filename:string) (root:RegistryKey) (key:Key) = | |
match key with | |
| (label, values) -> | |
use regkey = root.CreateSubKey(label) | |
values | |
|> Seq.map (ReMap filename) | |
|> Seq.iter regkey.SetValue | |
let MakeKey (filename:string) (key:Keys) = | |
match key with | |
| ((root, values), subkeys) -> | |
use regkey = Registry.CurrentUser.CreateSubKey(root) | |
values | |
|> Seq.map (ReMap filename) | |
|> Seq.iter regkey.SetValue | |
subkeys |> Seq.iter (MakeSubKey filename regkey) | |
let RegisterProfilerForUser filename = | |
keys | |
|> Seq.iter (MakeKey filename) | |
//------------------------------------------- | |
let ClearKey (key:Keys) = | |
match key with | |
| ((root, _), _) -> Registry.CurrentUser.DeleteSubKeyTree(root) | |
let UnregisterProfilerForUser () = | |
keys | |
|> Seq.iter ClearKey | |
//------------------------------------------- | |
if fsi.CommandLineArgs.Length > 1 then | |
RegisterProfilerForUser fsi.CommandLineArgs.[1] | |
else | |
UnregisterProfilerForUser () |
This updated per-user registration permits both building and running the PartCover utility without needing full administrator privilege.
So emitting
...\PartCover.exe --target "..\..\..\_Tools\Microsoft Fxcop 10.0\FxCopCmd.exe" --target-work-dir . --target-args "/q /s /console /f:Tinesware.Recorder.dll /f:Tinesware.Instrumentation.exe /f:Tinesware.Infrastructure.dll /f:Tinesware.Rules.dll /f:Tinesware.InfrastructureTests.dll /f:BaseTests.dll /rule:Tinesware.Rules .dll /o:fxcop.xml /dictionary:..\..\..\CustomDictionary.xml" --include [Tinesware.*]* --include [CSharpTests]* --output PartCoverage.xml
to monitor code coverage when using an FxCop rule written in F#, this reported
open driver pipe modify target environment variables create target process wait for driver connection [00000] [05724] Options dump: [00000] [05724] VerboseLevel: -842150451 [00016] [05724] Log file: ...\Infrastructure\_Binaries\Tinesware.InfrastructureTe sts4\Debug+AnyCPU\partcover.driver.log [00016] [05724] Log pipe: yes [00016] [05724] Count Coverage - ON [00016] [05724] Count Call Tree - OFF [00016] [05724] Exclude [mscorlib]* [00016] [05724] Exclude [System*]* [00016] [05724] Include [Tinesware.*]* [00016] [05724] Include [CSharpTests]* Project : warning : CA0060 : The indirectly-referenced assembly 'Microsoft.VisualStudio.CodeAnalysis, Version=10.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' could not be found. This assembly is not required for analysis, howeve r, analysis results could be incomplete. This assembly was referenced by: ...\Infra structure\_Binaries\Tinesware.InfrastructureTests4\Debug+AnyCPU\FxCopSdk.dll. [04774] [05724] CorProfiler is turned off Target PageFaultCount: 33536 Target PagefileUsage: 84246528 Target PeakPagefileUsage: 87359488 Target PeakWorkingSetSize: 92827648 Target QuotaNonPagedPoolUsage: 10920 Target QuotaPagedPoolUsage: 328336 Target QuotaPeakNonPagedPoolUsage: 43160 Target QuotaPeakPagedPoolUsage: 374344 Target WorkingSetSize: 89481216 Total 0 bytes ...\Infrastructure\_Binaries\Tinesware.InfrastructureTests4\Debug+AnyCPU
which completed cleanly, unlike before, and gave a sane looking output file. There is now obvious code in the system for doing the short branch instruction fix-up, which seems to have resolved the issue I had last time I tried this tool.
So, it really does look good to go for .net 4 code -- and I just have to add hooks to consume that format as well as the old NCover style.